You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2024/12/05 09:29:25 UTC
(tomcat) branch 9.0.x updated (84c4af76e7 -> d7c75b61ea)
This is an automated email from the ASF dual-hosted git repository.
remm pushed a change to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
from 84c4af76e7 Remove JSP calendar example.
new 27fe6a09c2 Add persistent PropertyStore for WebDAV
new d7c75b61ea jakarta -> javax
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../catalina/servlets/DataSourcePropertyStore.java | 356 +++++++++++++++++++++
.../catalina/servlets/LocalStrings.properties | 3 +
.../apache/catalina/servlets/WebdavServlet.java | 2 +-
java/org/apache/catalina/util/DOMWriter.java | 4 +
.../catalina/servlets/TestWebdavPropertyStore.java | 245 ++++++++++++++
webapps/docs/changelog.xml | 4 +
6 files changed, 613 insertions(+), 1 deletion(-)
create mode 100644 java/org/apache/catalina/servlets/DataSourcePropertyStore.java
create mode 100644 test/org/apache/catalina/servlets/TestWebdavPropertyStore.java
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 02/02: jakarta -> javax
Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit d7c75b61ea2ce298626b3ce40fa9f9e7e96d8bfe
Author: remm <re...@apache.org>
AuthorDate: Thu Dec 5 10:29:13 2024 +0100
jakarta -> javax
---
java/org/apache/catalina/servlets/DataSourcePropertyStore.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/servlets/DataSourcePropertyStore.java b/java/org/apache/catalina/servlets/DataSourcePropertyStore.java
index 397f365c81..25f8743561 100644
--- a/java/org/apache/catalina/servlets/DataSourcePropertyStore.java
+++ b/java/org/apache/catalina/servlets/DataSourcePropertyStore.java
@@ -29,7 +29,7 @@ import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
-import jakarta.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.servlets.WebdavServlet.PropertyUpdateType;
import org.apache.catalina.servlets.WebdavServlet.ProppatchOperation;
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 01/02: Add persistent PropertyStore for WebDAV
Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 27fe6a09c278e9bb3aeddf4047d23e7f999f9502
Author: remm <re...@apache.org>
AuthorDate: Thu Dec 5 10:25:40 2024 +0100
Add persistent PropertyStore for WebDAV
---
.../catalina/servlets/DataSourcePropertyStore.java | 356 +++++++++++++++++++++
.../catalina/servlets/LocalStrings.properties | 3 +
.../apache/catalina/servlets/WebdavServlet.java | 2 +-
java/org/apache/catalina/util/DOMWriter.java | 4 +
.../catalina/servlets/TestWebdavPropertyStore.java | 245 ++++++++++++++
webapps/docs/changelog.xml | 4 +
6 files changed, 613 insertions(+), 1 deletion(-)
diff --git a/java/org/apache/catalina/servlets/DataSourcePropertyStore.java b/java/org/apache/catalina/servlets/DataSourcePropertyStore.java
new file mode 100644
index 0000000000..397f365c81
--- /dev/null
+++ b/java/org/apache/catalina/servlets/DataSourcePropertyStore.java
@@ -0,0 +1,356 @@
+/*
+ * 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.StringWriter;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.servlets.WebdavServlet.PropertyUpdateType;
+import org.apache.catalina.servlets.WebdavServlet.ProppatchOperation;
+import org.apache.catalina.util.DOMWriter;
+import org.apache.catalina.util.XMLWriter;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+import org.w3c.dom.Node;
+
+/**
+ * WebDAV dead properties storage backed by a DataSource. Usually table and column names
+ * are configurable, but for simplicity this is not the case.
+ * The schema is:
+ * table properties ( path, namespace, name, node )
+ * path: the resource path
+ * namespace: the node namespace
+ * name: the local name in the namespace
+ * node: the full serialized XML node including the name
+ */
+public class DataSourcePropertyStore implements WebdavServlet.PropertyStore {
+
+ protected static final StringManager sm = StringManager.getManager(DataSourcePropertyStore.class);
+ private final Log log = LogFactory.getLog(DataSourcePropertyStore.class);
+
+ private static String ADD_PROPERTY_STMT = "INSERT INTO properties (path, namespace, name, node) VALUES (?, ?, ?, ?)";
+ private static String SET_PROPERTY_STMT = "UPDATE properties SET node = ? WHERE path = ? AND namespace = ? AND name = ?";
+ private static String REMOVE_ALL_PROPERTIES_STMT = "DELETE FROM properties WHERE path = ?";
+ private static String REMOVE_PROPERTY_STMT = "DELETE FROM properties WHERE path = ? AND namespace = ? AND name = ?";
+ private static String GET_PROPERTY_STMT = "SELECT node FROM properties WHERE path = ? AND namespace = ? AND name = ?";
+ private static String GET_PROPERTIES_NAMES_STMT = "SELECT namespace, name FROM properties WHERE path = ?";
+ private static String GET_PROPERTIES_STMT = "SELECT namespace, name, node FROM properties WHERE path = ?";
+ private static String GET_PROPERTIES_NODES_STMT = "SELECT node FROM properties WHERE path = ?";
+
+ /**
+ * DataSource JNDI name, will be prefixed with java:comp/env for the lookup.
+ */
+ private String dataSourceName = "WebdavPropertyStore";
+
+ private final ReentrantReadWriteLock dbLock = new ReentrantReadWriteLock();
+ private final Lock dbReadLock = dbLock.readLock();
+ private final Lock dbWriteLock = dbLock.writeLock();
+
+ /**
+ * @return the dataSourceName
+ */
+ public String getDataSourceName() {
+ return this.dataSourceName;
+ }
+
+ /**
+ * @param dataSourceName the dataSourceName to set
+ */
+ public void setDataSourceName(String dataSourceName) {
+ this.dataSourceName = dataSourceName;
+ }
+
+ /**
+ * DataSource instance being used.
+ */
+ protected DataSource dataSource = null;
+
+ @Override
+ public void init() {
+ if (dataSource == null) {
+ try {
+ dataSource = (DataSource) ((new InitialContext()).lookup("java:comp/env/" + dataSourceName));
+ } catch (NamingException e) {
+ throw new IllegalArgumentException(sm.getString("webdavservlet.dataSourceStore.noDataSource", dataSourceName), e);
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public void periodicEvent() {
+ }
+
+ @Override
+ public void copy(String source, String destination) {
+ if (dataSource == null) {
+ return;
+ }
+ dbWriteLock.lock();
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement statement = connection.prepareStatement(GET_PROPERTIES_STMT)) {
+ statement.setString(1, source);
+ if (statement.execute()) {
+ ResultSet rs = statement.getResultSet();
+ while (rs.next()) {
+ String namespace = rs.getString(1);
+ String name = rs.getString(2);
+ String node = rs.getString(3);
+ boolean found = false;
+ try (PreparedStatement statement2 = connection.prepareStatement(GET_PROPERTY_STMT)) {
+ statement2.setString(1, destination);
+ statement2.setString(2, namespace);
+ statement2.setString(3, name);
+ if (statement2.execute()) {
+ ResultSet rs2 = statement2.getResultSet();
+ if (rs2.next()) {
+ found = true;
+ }
+ }
+ }
+ if (found) {
+ try (PreparedStatement statement2 = connection.prepareStatement(SET_PROPERTY_STMT)) {
+ statement2.setString(1, node);
+ statement2.setString(2, destination);
+ statement2.setString(3, namespace);
+ statement2.setString(4, name);
+ statement2.execute();
+ }
+ } else {
+ try (PreparedStatement statement2 = connection.prepareStatement(ADD_PROPERTY_STMT)) {
+ statement2.setString(1, destination);
+ statement2.setString(2, namespace);
+ statement2.setString(3, name);
+ statement2.setString(4, node);
+ statement2.execute();
+ }
+ }
+ }
+ }
+ } catch (SQLException e) {
+ log.warn(sm.getString("webdavservlet.dataSourceStore.error", "copy", source), e);
+ } finally {
+ dbWriteLock.unlock();
+ }
+ }
+
+ @Override
+ public void delete(String resource) {
+ if (dataSource == null) {
+ return;
+ }
+ dbWriteLock.lock();
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement statement = connection.prepareStatement(REMOVE_ALL_PROPERTIES_STMT)) {
+ statement.setString(1, resource);
+ statement.execute();
+ } catch (SQLException e) {
+ log.warn(sm.getString("webdavservlet.dataSourceStore.error", "delete", resource), e);
+ } finally {
+ dbWriteLock.unlock();
+ }
+ }
+
+ @Override
+ public boolean propfind(String resource, Node property, boolean nameOnly, XMLWriter generatedXML) {
+ if (dataSource == null) {
+ return false;
+ }
+ if (nameOnly) {
+ // Add the names of all properties
+ dbReadLock.lock();
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement statement = connection.prepareStatement(GET_PROPERTIES_NAMES_STMT)) {
+ statement.setString(1, resource);
+ if (statement.execute()) {
+ ResultSet rs = statement.getResultSet();
+ while (rs.next()) {
+ String namespace = rs.getString(1);
+ String name = rs.getString(2);
+ generatedXML.writeElement(null, namespace, name, XMLWriter.NO_CONTENT);
+ }
+ }
+ } catch (SQLException e) {
+ log.warn(sm.getString("webdavservlet.dataSourceStore.error", "propfind", resource), e);
+ } finally {
+ dbReadLock.unlock();
+ }
+ } else if (property != null) {
+ // Add a single property
+ dbReadLock.lock();
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement statement = connection.prepareStatement(GET_PROPERTY_STMT)) {
+ statement.setString(1, resource);
+ statement.setString(2, property.getNamespaceURI());
+ statement.setString(3, property.getLocalName());
+ if (statement.execute()) {
+ ResultSet rs = statement.getResultSet();
+ if (rs.next()) {
+ String node = rs.getString(1);
+ generatedXML.writeRaw(node);
+ return true;
+ }
+ }
+ } catch (SQLException e) {
+ log.warn(sm.getString("webdavservlet.dataSourceStore.error", "propfind", resource), e);
+ } finally {
+ dbReadLock.unlock();
+ }
+ } else {
+ // Add all properties
+ dbReadLock.lock();
+ try (Connection connection = dataSource.getConnection();
+ PreparedStatement statement = connection.prepareStatement(GET_PROPERTIES_NODES_STMT)) {
+ statement.setString(1, resource);
+ if (statement.execute()) {
+ ResultSet rs = statement.getResultSet();
+ while (rs.next()) {
+ String node = rs.getString(1);
+ generatedXML.writeRaw(node);
+ }
+ }
+ } catch (SQLException e) {
+ log.warn(sm.getString("webdavservlet.dataSourceStore.error", "propfind", resource), e);
+ } finally {
+ dbReadLock.unlock();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void proppatch(String resource, ArrayList<ProppatchOperation> operations) {
+ boolean protectedProperty = false;
+ // Check for the protected properties
+ for (ProppatchOperation operation : operations) {
+ if (operation.getProtectedProperty()) {
+ protectedProperty = true;
+ operation.setStatusCode(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+ if (protectedProperty) {
+ for (ProppatchOperation operation : operations) {
+ if (!operation.getProtectedProperty()) {
+ operation.setStatusCode(WebdavStatus.SC_FAILED_DEPENDENCY);
+ }
+ }
+ } else {
+ if (dataSource == null) {
+ for (ProppatchOperation operation : operations) {
+ operation.setStatusCode(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
+ }
+ return;
+ }
+ boolean failure = false;
+ dbWriteLock.lock();
+ try (Connection connection = dataSource.getConnection()) {
+ connection.setAutoCommit(false);
+ for (ProppatchOperation operation : operations) {
+ if (operation.getUpdateType() == PropertyUpdateType.SET) {
+ Node node = operation.getPropertyNode().cloneNode(true);
+ StringWriter strWriter = new StringWriter();
+ DOMWriter domWriter = new DOMWriter(strWriter);
+ domWriter.print(node);
+ String serializedNode = strWriter.toString();
+ boolean found = false;
+ try {
+ try (PreparedStatement statement = connection.prepareStatement(GET_PROPERTY_STMT)) {
+ statement.setString(1, resource);
+ statement.setString(2, node.getNamespaceURI());
+ statement.setString(3, node.getLocalName());
+ if (statement.execute()) {
+ ResultSet rs = statement.getResultSet();
+ if (rs.next()) {
+ found = true;
+ }
+ }
+ }
+ if (found) {
+ try (PreparedStatement statement = connection.prepareStatement(SET_PROPERTY_STMT)) {
+ statement.setString(1, serializedNode);
+ statement.setString(2, resource);
+ statement.setString(3, node.getNamespaceURI());
+ statement.setString(4, node.getLocalName());
+ statement.execute();
+ }
+ } else {
+ try (PreparedStatement statement = connection.prepareStatement(ADD_PROPERTY_STMT)) {
+ statement.setString(1, resource);
+ statement.setString(2, node.getNamespaceURI());
+ statement.setString(3, node.getLocalName());
+ statement.setString(4, serializedNode);
+ statement.execute();
+ }
+ }
+ } catch (SQLException e) {
+ failure = true;
+ operation.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ break;
+ }
+ }
+ if (operation.getUpdateType() == PropertyUpdateType.REMOVE) {
+ Node node = operation.getPropertyNode();
+ try (PreparedStatement statement = connection.prepareStatement(REMOVE_PROPERTY_STMT)) {
+ statement.setString(1, resource);
+ statement.setString(2, node.getNamespaceURI());
+ statement.setString(3, node.getLocalName());
+ statement.execute();
+ } catch (SQLException e) {
+ failure = true;
+ operation.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ break;
+ }
+ }
+ }
+ if (failure) {
+ connection.rollback();
+ for (ProppatchOperation operation : operations) {
+ if (operation.getStatusCode() == HttpServletResponse.SC_OK) {
+ operation.setStatusCode(WebdavStatus.SC_FAILED_DEPENDENCY);
+ }
+ }
+ } else {
+ connection.commit();
+ }
+ } catch (SQLException e) {
+ log.warn(sm.getString("webdavservlet.dataSourceStore.error", "proppatch", resource), e);
+ for (ProppatchOperation operation : operations) {
+ operation.setStatusCode(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
+ }
+ } finally {
+ dbWriteLock.unlock();
+ }
+ }
+ }
+
+}
diff --git a/java/org/apache/catalina/servlets/LocalStrings.properties b/java/org/apache/catalina/servlets/LocalStrings.properties
index fecf44bbcb..f60c50a7e1 100644
--- a/java/org/apache/catalina/servlets/LocalStrings.properties
+++ b/java/org/apache/catalina/servlets/LocalStrings.properties
@@ -62,3 +62,6 @@ webdavservlet.memorystore=Non persistent memory storage will be used for dead pr
webdavservlet.noStoreParameter=Init parameter [{0}] with value [{1}] was not found on the configured store
webdavservlet.nonWildcardMapping=The mapping [{0}] is not a wildcard mapping and should not be used for the WebDAV Servlet
webdavservlet.storeError=Error creating store of class [{0}], the default memory store will be used instead
+
+webdavservlet.dataSourceStore.error=Error processing [{0}] on dead properties for path [{1}]
+webdavservlet.dataSourceStore.noDataSource=DataSource [{0}] was not found in the webapp environment
diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java
index 805f64a229..cd0401b0f5 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -2887,7 +2887,7 @@ public class WebdavServlet extends DefaultServlet implements PeriodicEventListen
/**
* Default property store, which provides memory storage without persistence.
*/
- private class MemoryPropertyStore implements PropertyStore {
+ public static class MemoryPropertyStore implements PropertyStore {
private final ConcurrentHashMap<String,ArrayList<Node>> deadProperties = new ConcurrentHashMap<>();
diff --git a/java/org/apache/catalina/util/DOMWriter.java b/java/org/apache/catalina/util/DOMWriter.java
index 7ef95b1ffa..abedc0355b 100644
--- a/java/org/apache/catalina/util/DOMWriter.java
+++ b/java/org/apache/catalina/util/DOMWriter.java
@@ -66,6 +66,10 @@ public class DOMWriter {
Attr attrs[] = sortAttributes(node.getAttributes());
boolean xmlns = false;
for (Attr attr : attrs) {
+ if ("xmlns".equals(attr.getPrefix())) {
+ // Skip namespace prefixes as they are removed
+ continue;
+ }
out.print(' ');
out.print(attr.getLocalName());
if ("xmlns".equals(attr.getLocalName())) {
diff --git a/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java b/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java
new file mode 100644
index 0000000000..3f03ff710e
--- /dev/null
+++ b/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java
@@ -0,0 +1,245 @@
+/*
+ * 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\" ?>\n" +
+ "<T:customprop xmlns:T=\"http://tomcat.apache.org/testsuite\">\n" +
+ " <T:myvalue/>\n" +
+ "</T:customprop>";
+
+ private static final String PROPERTY2 =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<V:someprop xmlns:V=\"http://tomcat.apache.org/other\">\n" +
+ " <V:myvalue>bla</V:myvalue>\n" +
+ "</V:someprop>";
+
+ private static final String PROPERTY3 =
+ "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
+ "<V:someprop xmlns:V=\"http://tomcat.apache.org/other\">\n" +
+ " <V:othervalue>foooooooo</V:othervalue>\n" +
+ "</V:someprop>";
+
+ public static final String SIMPLE_SCHEMA =
+ "create table properties (\n" +
+ " path varchar(256) not null,\n" +
+ " namespace varchar(64) not null,\n" +
+ " name varchar(64) not null,\n" +
+ " node varchar(1024) not null" +
+ ")";
+
+ 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());
+ }
+
+ // 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());
+
+ }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 865595dfac..2dad54765d 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -218,6 +218,10 @@
the default servlet. It will be removed in Tomcat 12 onwards where it
will effectively be hard coded to <code>true</code>. (markt)
</fix>
+ <add>
+ Add <code>DataSource</code> based property storage for the
+ <code>WebdavServlet</code>. (remm)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org