This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 2fb103ba4c2f52f428f2c31ae6366e40425d824e Author: opencode <[email protected]> AuthorDate: Wed Oct 7 16:53:20 2026 +0200 Store namespace-less WebDAV properties in DataSourcePropertyStore Node.getNamespaceURI() returns null for a property element without a namespace, which was passed directly into the namespace column of the queries. With the documented NOT NULL schema every SET, REMOVE and single-property PROPFIND of a namespace-less property such as a plain <displayname> failed with a SQLException and a 500 response, while the memory store handled these properties without issue. Normalise a null namespace to the empty string, which the SQL statements and the XMLWriter output handle consistently, making the two PropertyStore implementations agree. --- .../catalina/servlets/DataSourcePropertyStore.java | 13 +++++---- .../catalina/servlets/TestWebdavPropertyStore.java | 33 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/java/org/apache/catalina/servlets/DataSourcePropertyStore.java b/java/org/apache/catalina/servlets/DataSourcePropertyStore.java index ee900662a6..b2031a6463 100644 --- a/java/org/apache/catalina/servlets/DataSourcePropertyStore.java +++ b/java/org/apache/catalina/servlets/DataSourcePropertyStore.java @@ -22,6 +22,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Objects; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -46,7 +47,7 @@ import org.w3c.dom.Node; * A single properties table with four columns is used: * <ul> * <li>path: the resource path</li> - * <li>namespace: the node namespace</li> + * <li>namespace: the node namespace, or the empty string when the property has no namespace</li> * <li>name: the local name in the namespace</li> * <li>node: the full serialized XML node including the name</li> * </ul> @@ -266,7 +267,7 @@ public class DataSourcePropertyStore implements WebdavServlet.PropertyStore { try (Connection connection = dataSource.getConnection(); PreparedStatement statement = connection.prepareStatement(getPropertyStatement)) { statement.setString(1, resource); - statement.setString(2, property.getNamespaceURI()); + statement.setString(2, Objects.requireNonNullElse(property.getNamespaceURI(), "")); statement.setString(3, property.getLocalName()); if (statement.execute()) { ResultSet rs = statement.getResultSet(); @@ -341,7 +342,7 @@ public class DataSourcePropertyStore implements WebdavServlet.PropertyStore { try { try (PreparedStatement statement = connection.prepareStatement(getPropertyStatement)) { statement.setString(1, resource); - statement.setString(2, node.getNamespaceURI()); + statement.setString(2, Objects.requireNonNullElse(node.getNamespaceURI(), "")); statement.setString(3, node.getLocalName()); if (statement.execute()) { ResultSet rs = statement.getResultSet(); @@ -354,14 +355,14 @@ public class DataSourcePropertyStore implements WebdavServlet.PropertyStore { try (PreparedStatement statement = connection.prepareStatement(setPropertyStatement)) { statement.setString(1, serializedNode); statement.setString(2, resource); - statement.setString(3, node.getNamespaceURI()); + statement.setString(3, Objects.requireNonNullElse(node.getNamespaceURI(), "")); statement.setString(4, node.getLocalName()); statement.execute(); } } else { try (PreparedStatement statement = connection.prepareStatement(addPropertyStatement)) { statement.setString(1, resource); - statement.setString(2, node.getNamespaceURI()); + statement.setString(2, Objects.requireNonNullElse(node.getNamespaceURI(), "")); statement.setString(3, node.getLocalName()); statement.setString(4, serializedNode); statement.execute(); @@ -377,7 +378,7 @@ public class DataSourcePropertyStore implements WebdavServlet.PropertyStore { Node node = operation.getPropertyNode(); try (PreparedStatement statement = connection.prepareStatement(removePropertyStatement)) { statement.setString(1, resource); - statement.setString(2, node.getNamespaceURI()); + statement.setString(2, Objects.requireNonNullElse(node.getNamespaceURI(), "")); statement.setString(3, node.getLocalName()); statement.execute(); } catch (SQLException e) { diff --git a/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java b/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java index 651c198f71..65b79f17c7 100644 --- a/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java +++ b/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java @@ -33,6 +33,8 @@ import javax.sql.DataSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import jakarta.servlet.http.HttpServletResponse; + import org.junit.Assert; import org.junit.Assume; import org.junit.Test; @@ -71,6 +73,10 @@ public class TestWebdavPropertyStore extends LoggingBaseTest { " <V:othervalue>foooooooo</V:othervalue>\n" + "</V:someprop>"; + private static final String PROPERTY4 = + "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + + "<displayname>test value</displayname>"; + public static final String SIMPLE_SCHEMA = "CREATE TABLE webdavproperties (\n" + " path VARCHAR(1024) NOT NULL,\n" + @@ -250,6 +256,33 @@ public class TestWebdavPropertyStore extends LoggingBaseTest { Assert.assertFalse(propertyStore.propfind("/other/path2", node1, false, xmlWriter9)); Assert.assertTrue(xmlWriter9.toString().isEmpty()); + // Set a property without a namespace + Document document4 = documentBuilder.parse(new InputSource(new ByteArrayInputStream(PROPERTY4.getBytes(StandardCharsets.UTF_8)))); + Node node4 = document4.getDocumentElement(); + + operations = new ArrayList<>(); + operations.add(new ProppatchOperation(PropertyUpdateType.SET, node4)); + propertyStore.proppatch("/some/path1", operations); + Assert.assertEquals(HttpServletResponse.SC_OK, operations.get(0).getStatusCode()); + + XMLWriter xmlWriter10 = new XMLWriter(); + Assert.assertTrue(propertyStore.propfind("/some/path1", node4, false, xmlWriter10)); + Assert.assertTrue(xmlWriter10.toString().contains(">test value<")); + + XMLWriter xmlWriter11 = new XMLWriter(); + Assert.assertFalse(propertyStore.propfind("/some/path1", null, true, xmlWriter11)); + Assert.assertTrue(xmlWriter11.toString().contains("<displayname")); + + // Remove the property without a namespace + operations = new ArrayList<>(); + operations.add(new ProppatchOperation(PropertyUpdateType.REMOVE, node4)); + propertyStore.proppatch("/some/path1", operations); + Assert.assertEquals(HttpServletResponse.SC_OK, operations.get(0).getStatusCode()); + + XMLWriter xmlWriter12 = new XMLWriter(); + Assert.assertFalse(propertyStore.propfind("/some/path1", null, true, xmlWriter12)); + Assert.assertFalse(xmlWriter12.toString().contains("<displayname")); + propertyStore.destroy(); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
