/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.servlets;
import java.io.ByteArrayInputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.servlets.WebdavServlet.PropertyStore;
import org.apache.catalina.servlets.WebdavServlet.PropertyUpdateType;
import org.apache.catalina.servlets.WebdavServlet.ProppatchOperation;
import org.apache.catalina.startup.LoggingBaseTest;
import org.apache.catalina.util.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
@RunWith(Parameterized.class)
public class TestWebdavPropertyStore extends LoggingBaseTest {
private static final String PROPERTY1 =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?> " +
"<T:customprop xmlns:T=\"http://tomcat.apache.org/testsuite\"> " +
" <T:myvalue/> " +
"</T:customprop>";
private static final String PROPERTY2 =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?> " +
"<V:someprop xmlns:V=\"http://tomcat.apache.org/other\"> " +
" <V:myvalue>bla</V:myvalue> " +
"</V:someprop>";
private static final String PROPERTY3 =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?> " +
"<V:someprop xmlns:V=\"http://tomcat.apache.org/other\"> " +
" <V:othervalue>foooooooo</V:othervalue> " +
"</V:someprop>";
public static final String SIMPLE_SCHEMA =
"CREATE TABLE webdavproperties ( " +
" path VARCHAR(1024) NOT NULL, " +
" namespace VARCHAR(64) NOT NULL, " +
" name VARCHAR(64) NOT NULL, " +
" node VARCHAR(2048) NOT NULL, " +
" PRIMARY KEY (path, namespace, name) " +
")";
public static class CustomDataSourcePropertyStore extends DataSourcePropertyStore {
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
private class DerbyDataSource implements DataSource {
Connection connection = null;
DerbyDataSource() {
try {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
connection = DriverManager.getConnection("jdbc:derby:" + getTemporaryDirectory().getAbsolutePath()
+ "/webdavproperties;create=true");
try (Statement statement = connection.createStatement()) {
statement.execute(SIMPLE_SCHEMA);
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public Connection getConnection() throws SQLException {
if (connection.isClosed()) {
connection = DriverManager.getConnection("jdbc:derby:" + getTemporaryDirectory().getAbsolutePath()
+ "/webdavproperties");
}
return connection;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return getConnection();
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
}
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
parameterSets.add(new Object[] { "org.apache.catalina.servlets.WebdavServlet$MemoryPropertyStore" });
parameterSets.add(new Object[] { "org.apache.catalina.servlets.TestWebdavPropertyStore$CustomDataSourcePropertyStore" });
return parameterSets;
}
@Parameter(0)
public String storeName;
@Test
public void testStore() throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setExpandEntityReferences(false);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document1 = documentBuilder.parse(new InputSource(new ByteArrayInputStream(PROPERTY1.getBytes(StandardCharsets.UTF_8))));
Node node1 = document1.getDocumentElement();
Document document2 = documentBuilder.parse(new InputSource(new ByteArrayInputStream(PROPERTY2.getBytes(StandardCharsets.UTF_8))));
Node node2 = document2.getDocumentElement();
Document document3 = documentBuilder.parse(new InputSource(new ByteArrayInputStream(PROPERTY3.getBytes(StandardCharsets.UTF_8))));
Node node3 = document3.getDocumentElement();
PropertyStore propertyStore = (PropertyStore) Class.forName(storeName).getDeclaredConstructor().newInstance();
if (propertyStore instanceof CustomDataSourcePropertyStore) {
((CustomDataSourcePropertyStore) propertyStore).setDataSource(new DerbyDataSource());
((CustomDataSourcePropertyStore) propertyStore).setTableName("webdavproperties");
}
propertyStore.init();
// Add properties
ArrayList<ProppatchOperation> operations = new ArrayList<>();
operations.add(new ProppatchOperation(PropertyUpdateType.SET, node1));
operations.add(new ProppatchOperation(PropertyUpdateType.SET, node2));
propertyStore.proppatch("/some/path1", operations);
// Add properties
operations = new ArrayList<>();
operations.add(new ProppatchOperation(PropertyUpdateType.SET, node1));
propertyStore.proppatch("/other/path2", operations);
// Get single property
XMLWriter xmlWriter1 = new XMLWriter();
Assert.assertTrue(propertyStore.propfind("/some/path1", node1, false, xmlWriter1));
Assert.assertTrue(xmlWriter1.toString().contains("<myvalue "));
// Get property names
XMLWriter xmlWriter2 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/some/path1", null, true, xmlWriter2));
Assert.assertTrue(xmlWriter2.toString().contains("<someprop"));
// Get all properties
XMLWriter xmlWriter3 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/some/path1", null, false, xmlWriter3));
Assert.assertTrue(xmlWriter3.toString().contains(">bla</myvalue>"));
propertyStore.copy("/some/path1", "/some/path2");
XMLWriter xmlWriter4 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/some/path2", null, true, xmlWriter4));
Assert.assertTrue(xmlWriter4.toString().contains("<someprop"));
propertyStore.delete("/some/path1");
XMLWriter xmlWriter5 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/some/path1", null, true, xmlWriter5));
Assert.assertTrue(xmlWriter5.toString().isEmpty());
propertyStore.copy("/some/path2", "/other/path2");
XMLWriter xmlWriter6 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/other/path2", null, true, xmlWriter6));
Assert.assertTrue(xmlWriter6.toString().contains("<customprop"));
operations = new ArrayList<>();
operations.add(new ProppatchOperation(PropertyUpdateType.REMOVE, node1));
propertyStore.proppatch("/other/path2", operations);
XMLWriter xmlWriter7 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/other/path2", null, false, xmlWriter7));
Assert.assertFalse(xmlWriter7.toString().contains("<customprop"));
Assert.assertTrue(xmlWriter7.toString().contains(">bla</myvalue>"));
operations = new ArrayList<>();
operations.add(new ProppatchOperation(PropertyUpdateType.SET, node3));
propertyStore.proppatch("/other/path2", operations);
XMLWriter xmlWriter8 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/other/path2", null, false, xmlWriter8));
Assert.assertFalse(xmlWriter8.toString().contains("<customprop"));
Assert.assertTrue(xmlWriter8.toString().contains(">foooooooo</othervalue>"));
XMLWriter xmlWriter9 = new XMLWriter();
Assert.assertFalse(propertyStore.propfind("/other/path2", node1, false, xmlWriter9));
Assert.assertTrue(xmlWriter9.toString().isEmpty());
propertyStore.destroy();
}
}