This is an automated email from the ASF dual-hosted git repository.
remm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new 40c094b8ba Refine the dead properties handling
40c094b8ba is described below
commit 40c094b8ba52513b1468a45db0d9ad0c36a64fbf
Author: remm <[email protected]>
AuthorDate: Sun Oct 20 23:46:05 2024 +0200
Refine the dead properties handling
Add a proof of concept otherwise it is not possible to validate it.
Add namespaces on elments to the DOMWriter.
---
.../servlets/TransientPropertiesWebdavServlet.java | 186 +++++++++++++++++++++
.../apache/catalina/servlets/WebdavServlet.java | 71 ++++++--
java/org/apache/catalina/util/DOMWriter.java | 12 +-
.../catalina/servlets/TestWebdavServlet.java | 84 ++++------
4 files changed, 286 insertions(+), 67 deletions(-)
diff --git
a/java/org/apache/catalina/servlets/TransientPropertiesWebdavServlet.java
b/java/org/apache/catalina/servlets/TransientPropertiesWebdavServlet.java
new file mode 100644
index 0000000000..80c979c190
--- /dev/null
+++ b/java/org/apache/catalina/servlets/TransientPropertiesWebdavServlet.java
@@ -0,0 +1,186 @@
+/*
+ * 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.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.util.DOMWriter;
+import org.apache.catalina.util.XMLWriter;
+import org.w3c.dom.Node;
+
+/**
+ * Extended WebDAV Servlet that implements dead properties using storage in
memory.
+ */
+public class TransientPropertiesWebdavServlet extends WebdavServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ private final ConcurrentHashMap<String,ArrayList<Node>> deadProperties =
new ConcurrentHashMap<>();
+
+ @Override
+ protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
+ resp.addHeader("DAV", "1,2,3");
+ resp.addHeader("Allow", determineMethodsAllowed(req));
+ resp.addHeader("MS-Author-Via", "DAV");
+ }
+
+ @Override
+ protected void proppatchResource(String path,
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 {
+ ArrayList<Node> properties = deadProperties.get(path);
+ if (properties == null) {
+ properties = new ArrayList<Node>();
+ deadProperties.put(path, properties);
+ }
+ synchronized (properties) {
+ for (ProppatchOperation operation : operations) {
+ if (operation.getUpdateType() == PropertyUpdateType.SET) {
+ Node node =
operation.getPropertyNode().cloneNode(true);
+ boolean found = false;
+ for (int i = 0; i < properties.size(); i++) {
+ Node propertyNode = properties.get(i);
+ if (propertyEquals(node, propertyNode)) {
+ found = true;
+ properties.set(i, node);
+ break;
+ }
+ }
+ if (!found) {
+ properties.add(node);
+ }
+ } if (operation.getUpdateType() ==
PropertyUpdateType.REMOVE) {
+ Node node = operation.getPropertyNode();
+ for (int i = 0; i < properties.size(); i++) {
+ Node propertyNode = properties.get(i);
+ if (propertyEquals(node, propertyNode)) {
+ properties.remove(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected boolean propfindResource(String path, Node property, boolean
nameOnly, XMLWriter generatedXML) {
+ ArrayList<Node> properties = deadProperties.get(path);
+ if (properties != null) {
+ synchronized (properties) {
+ if (nameOnly) {
+ // Add the names of all properties
+ for (Node node : properties) {
+ generatedXML.writeElement(node.getPrefix(),
+ node.getNamespaceURI(),
+ node.getLocalName(), XMLWriter.NO_CONTENT);
+ }
+ } else if (property != null) {
+ // Add a single property
+ Node foundNode = null;
+ for (Node node : properties) {
+ if (propertyEquals(node, property)) {
+ foundNode = node;
+ }
+ }
+ if (foundNode != null) {
+ StringWriter strWriter = new StringWriter();
+ DOMWriter domWriter = new DOMWriter(strWriter);
+ domWriter.print(foundNode);
+ generatedXML.writeRaw(strWriter.toString());
+ return true;
+ }
+ } else {
+ StringWriter strWriter = new StringWriter();
+ DOMWriter domWriter = new DOMWriter(strWriter);
+ // Add all properties
+ for (Node node : properties) {
+ domWriter.print(node);
+ }
+ generatedXML.writeRaw(strWriter.toString());
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void copyResource(String source, String dest) {
+ ArrayList<Node> properties = deadProperties.get(source);
+ ArrayList<Node> propertiesDest = deadProperties.get(dest);
+ if (properties != null) {
+ if (propertiesDest == null) {
+ propertiesDest = new ArrayList<Node>();
+ deadProperties.put(dest, propertiesDest);
+ }
+ synchronized (properties) {
+ synchronized (propertiesDest) {
+ for (Node node : properties) {
+ node = node.cloneNode(true);
+ boolean found = false;
+ for (int i = 0; i < propertiesDest.size(); i++) {
+ Node propertyNode = propertiesDest.get(i);
+ if (propertyEquals(node, propertyNode)) {
+ found = true;
+ propertiesDest.set(i, node);
+ break;
+ }
+ }
+ if (!found) {
+ propertiesDest.add(node);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void deleteResource(String path) {
+ deadProperties.remove(path);
+ }
+
+ private boolean propertyEquals(Node node1, Node node2) {
+ if (node1.getLocalName().equals(node2.getLocalName())
+ && ((node1.getNamespaceURI() == null &&
node2.getNamespaceURI() == null)
+ || (node1.getNamespaceURI() != null &&
node1.getNamespaceURI().equals(node2.getNamespaceURI())))) {
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java
b/java/org/apache/catalina/servlets/WebdavServlet.java
index 15f5d398d6..4f1df32636 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -998,7 +998,7 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
/**
- * Apply proppatch to the specified path. This should be overriden by
subclasses to provide
+ * Apply proppatch to the specified path. This should be overridden by
subclasses to provide
* useful behavior. The default implementation prevents setting protected
properties
* (anything from the DAV: namespace), and sets 507 for a set attempt on
dead properties.
*
@@ -1964,6 +1964,8 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
errorList.put(dest,
Integer.valueOf(WebdavStatus.SC_CONFLICT));
return false;
}
+ } else {
+ copyResource(source, dest);
}
if (infiniteCopy) {
@@ -2006,6 +2008,8 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
if (!resources.write(dest, is, false)) {
errorList.put(source,
Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
return false;
+ } else {
+ copyResource(source, dest);
}
} catch (IOException e) {
log(sm.getString("webdavservlet.inputstreamclosefail",
source), e);
@@ -2017,6 +2021,16 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
return true;
}
+ /**
+ * Copy resource. This should be overridden by subclasses to provide
+ * useful behavior. The default implementation prevents setting protected
properties
+ * (anything from the DAV: namespace), and sets 507 for a set attempt on
dead properties.
+ *
+ * @param source the copy source path
+ * @param dest the copy destination path
+ */
+ protected void copyResource(String source, String dest) {
+ }
/**
* Delete a resource.
@@ -2071,7 +2085,7 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
sendNotAllowed(req, resp);
return false;
}
- unlockResource(path, null);
+ deleteResource(path);
} else {
Map<String,Integer> errorList = new LinkedHashMap<>();
@@ -2094,7 +2108,7 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
errorList.put(path,
Integer.valueOf(WebdavStatus.SC_METHOD_NOT_ALLOWED));
}
} else {
- unlockResource(path, null);
+ deleteResource(path);
}
if (!errorList.isEmpty()) {
@@ -2108,6 +2122,17 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
return true;
}
+ /**
+ * Delete specified resource. This should be overridden by subclasses to
provide
+ * useful behavior. The default implementation prevents setting protected
properties
+ * (anything from the DAV: namespace), and sets 507 for a set attempt on
dead properties.
+ *
+ * @param path the path of the resource to delete
+ */
+ protected void deleteResource(String path) {
+ unlockResource(path, null);
+ }
+
private void unlockResource(String path, String lockToken) {
LockInfo lock = resourceLocks.get(path);
if (lock != null) {
@@ -2195,7 +2220,7 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
errorList.put(childName,
Integer.valueOf(WebdavStatus.SC_METHOD_NOT_ALLOWED));
}
} else {
- unlockResource(childName, null);
+ deleteResource(childName);
}
}
}
@@ -2299,9 +2324,6 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
generatedXML.writeProperty("D", "creationdate",
getISOCreationDate(created));
- generatedXML.writeElement("D", "displayname",
XMLWriter.OPENING);
- generatedXML.writeData(resourceName);
- generatedXML.writeElement("D", "displayname",
XMLWriter.CLOSING);
if (isFile) {
generatedXML.writeProperty("D", "getlastmodified",
FastHttpDateFormat.formatDate(lastModified));
generatedXML.writeProperty("D", "getcontentlength",
Long.toString(contentLength));
@@ -2338,7 +2360,6 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
generatedXML.writeElement("D", "prop", XMLWriter.OPENING);
generatedXML.writeElement("D", "creationdate",
XMLWriter.NO_CONTENT);
- generatedXML.writeElement("D", "displayname",
XMLWriter.NO_CONTENT);
if (isFile) {
generatedXML.writeElement("D", "getcontentlanguage",
XMLWriter.NO_CONTENT);
generatedXML.writeElement("D", "getcontentlength",
XMLWriter.NO_CONTENT);
@@ -2468,7 +2489,9 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
/**
- * Generate propfind XML fragments for dead properties.
+ * Generate propfind XML fragments for dead properties. This should be
overridden by subclasses to provide
+ * useful behavior. The default implementation prevents setting protected
properties
+ * (anything from the DAV: namespace), and sets 507 for a set attempt on
dead properties.
*
* @param path the resource path
* @param property the dead property, if null then all dead properties
must be written
@@ -2477,6 +2500,30 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
* @return true if property was specified and a corresponding dead
property was found on the resource, false otherwise
*/
protected boolean propfindResource(String path, Node property, boolean
nameOnly, XMLWriter generatedXML) {
+ if (nameOnly) {
+ generatedXML.writeElement("D", "displayname",
XMLWriter.NO_CONTENT);
+ } else if (property == null) {
+ String resourceName = path;
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash != -1) {
+ resourceName = resourceName.substring(lastSlash + 1);
+ }
+ generatedXML.writeElement("D", "displayname", XMLWriter.OPENING);
+ generatedXML.writeData(resourceName);
+ generatedXML.writeElement("D", "displayname", XMLWriter.CLOSING);
+ } else {
+ String davName = getDAVNode(property);
+ if ("displayname".equals(davName)) {
+ String resourceName = path;
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash != -1) {
+ resourceName = resourceName.substring(lastSlash + 1);
+ }
+ generatedXML.writeElement("D", "displayname",
XMLWriter.OPENING);
+ generatedXML.writeData(resourceName);
+ generatedXML.writeElement("D", "displayname",
XMLWriter.CLOSING);
+ }
+ }
return false;
}
@@ -2575,7 +2622,7 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
private static String getDAVNode(Node node) {
- if (node.getNamespaceURI().equals(DEFAULT_NAMESPACE)) {
+ if (DEFAULT_NAMESPACE.equals(node.getNamespaceURI())) {
return node.getLocalName();
}
return null;
@@ -2731,7 +2778,9 @@ public class WebdavServlet extends DefaultServlet
implements PeriodicEventListen
public ProppatchOperation(PropertyUpdateType updateType, Node
propertyNode) {
this.updateType = updateType;
this.propertyNode = propertyNode;
- protectedProperty = getDAVNode(propertyNode) != null;
+ String davName = getDAVNode(propertyNode);
+ protectedProperty = davName != null
+ && (!(davName.equals("displayname") ||
davName.equals("getcontentlanguage")));
}
/**
* @return the updateType
diff --git a/java/org/apache/catalina/util/DOMWriter.java
b/java/org/apache/catalina/util/DOMWriter.java
index 0cd3532526..7ef95b1ffa 100644
--- a/java/org/apache/catalina/util/DOMWriter.java
+++ b/java/org/apache/catalina/util/DOMWriter.java
@@ -64,14 +64,22 @@ public class DOMWriter {
out.print('<');
out.print(node.getLocalName());
Attr attrs[] = sortAttributes(node.getAttributes());
+ boolean xmlns = false;
for (Attr attr : attrs) {
out.print(' ');
out.print(attr.getLocalName());
-
+ if ("xmlns".equals(attr.getLocalName())) {
+ xmlns = true;
+ }
out.print("=\"");
out.print(Escape.xml("", true, attr.getNodeValue()));
out.print('"');
}
+ if (!xmlns && node.getNamespaceURI() != null) {
+ out.print(" xmlns=\"");
+ out.print(Escape.xml(node.getNamespaceURI()));
+ out.print('"');
+ }
out.print('>');
printChildren(node);
break;
@@ -88,7 +96,7 @@ public class DOMWriter {
// print text
case Node.TEXT_NODE:
- out.print(Escape.xml("", true, node.getNodeValue()));
+ out.print(Escape.xml("", false, node.getNodeValue()));
break;
// print processing instruction
diff --git a/test/org/apache/catalina/servlets/TestWebdavServlet.java
b/test/org/apache/catalina/servlets/TestWebdavServlet.java
index 8272381a56..71f7977ae4 100644
--- a/test/org/apache/catalina/servlets/TestWebdavServlet.java
+++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java
@@ -20,7 +20,6 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -36,7 +35,6 @@ import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.catalina.util.XMLWriter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.websocket.server.WsContextListener;
import org.xml.sax.InputSource;
@@ -230,7 +228,7 @@ public class TestWebdavServlet extends TomcatBaseTest {
"<D:propertyupdate xmlns:D=\"DAV:\"
xmlns:T=\"http://tomcat.apache.org/testsuite\">\n" +
" <D:set>\n" +
" <D:prop>\n" +
- " <T:customprop
xmlns:T=\"http://tomcat.apache.org/testsuite\">\n" +
+ " <T:customprop>\n" +
" <T:myvalue/>\n" +
" </T:customprop>\n" +
" </D:prop>\n" +
@@ -248,7 +246,7 @@ public class TestWebdavServlet extends TomcatBaseTest {
File tempWebapp = new File(getTemporaryDirectory(),
"webdav-properties");
Assert.assertTrue(tempWebapp.mkdirs());
Context ctxt = tomcat.addContext("", tempWebapp.getAbsolutePath());
- Wrapper webdavServlet = Tomcat.addServlet(ctxt, "webdav", new
CustomWebdavServlet());
+ Wrapper webdavServlet = Tomcat.addServlet(ctxt, "webdav", new
TransientPropertiesWebdavServlet());
webdavServlet.addInitParameter("listings", "true");
webdavServlet.addInitParameter("secret", "foo");
webdavServlet.addInitParameter("readonly", "false");
@@ -289,7 +287,17 @@ public class TestWebdavServlet extends TomcatBaseTest {
Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS,
client.getStatusCode());
Assert.assertTrue(client.getResponseBody().contains("opaquelocktoken:"));
- client.setRequest(new String[] { "PROPFIND / HTTP/1.1" +
SimpleHttpClient.CRLF +
+ client.setRequest(new String[] { "PROPPATCH /file1.txt HTTP/1.1" +
SimpleHttpClient.CRLF +
+ "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+ "Content-Length: " + PROPPATCH_PROPNAME.length() +
SimpleHttpClient.CRLF +
+ "Connection: Close" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF + PROPPATCH_PROPNAME });
+ client.connect();
+ client.processRequest(true);
+ Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS,
client.getStatusCode());
+
Assert.assertTrue(client.getResponseBody().contains("<T:othercustomprop"));
+
+ client.setRequest(new String[] { "PROPFIND /file1.txt HTTP/1.1" +
SimpleHttpClient.CRLF +
"Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
"Content-Length: " + PROPFIND_PROPNAME.length() +
SimpleHttpClient.CRLF +
"Connection: Close" + SimpleHttpClient.CRLF +
@@ -299,7 +307,7 @@ public class TestWebdavServlet extends TomcatBaseTest {
Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS,
client.getStatusCode());
Assert.assertTrue(client.getResponseBody().contains("<D:getcontenttype/>"));
- client.setRequest(new String[] { "PROPFIND / HTTP/1.1" +
SimpleHttpClient.CRLF +
+ client.setRequest(new String[] { "PROPFIND /file1.txt HTTP/1.1" +
SimpleHttpClient.CRLF +
"Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
"Content-Length: " + PROPFIND_PROP.length() +
SimpleHttpClient.CRLF +
"Connection: Close" + SimpleHttpClient.CRLF +
@@ -309,18 +317,28 @@ public class TestWebdavServlet extends TomcatBaseTest {
Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS,
client.getStatusCode());
Assert.assertTrue(client.getResponseBody().contains("<D:getcontenttype>"));
Assert.assertFalse(client.getResponseBody().contains("<D:getlastmodified>"));
- Assert.assertTrue(client.getResponseBody().contains("<T:myvalue/>"));
+ Assert.assertTrue(client.getResponseBody().contains("<myvalue
xmlns=\"http://tomcat.apache.org/testsuite\">"));
- client.setRequest(new String[] { "PROPPATCH /file1.txt HTTP/1.1" +
SimpleHttpClient.CRLF +
+ client.setRequest(new String[] { "MOVE /file1.txt HTTP/1.1" +
SimpleHttpClient.CRLF +
"Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
- "Content-Length: " + PROPPATCH_PROPNAME.length() +
SimpleHttpClient.CRLF +
+ "Destination: /file3.txt" + SimpleHttpClient.CRLF +
"Connection: Close" + SimpleHttpClient.CRLF +
- SimpleHttpClient.CRLF + PROPPATCH_PROPNAME });
+ SimpleHttpClient.CRLF });
+ client.connect();
+ client.processRequest(true);
+ Assert.assertEquals(HttpServletResponse.SC_CREATED,
client.getStatusCode());
+
+ client.setRequest(new String[] { "PROPFIND /file3.txt HTTP/1.1" +
SimpleHttpClient.CRLF +
+ "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+ "Content-Length: " + PROPFIND_PROP.length() +
SimpleHttpClient.CRLF +
+ "Connection: Close" + SimpleHttpClient.CRLF +
+ SimpleHttpClient.CRLF + PROPFIND_PROP });
client.connect();
client.processRequest(true);
Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS,
client.getStatusCode());
- Assert.assertTrue(proppatchSuccess);
-
Assert.assertTrue(client.getResponseBody().contains("<T:othercustomprop"));
+
Assert.assertTrue(client.getResponseBody().contains("<D:getcontenttype>"));
+
Assert.assertFalse(client.getResponseBody().contains("<D:getlastmodified>"));
+ Assert.assertTrue(client.getResponseBody().contains("<myvalue
xmlns=\"http://tomcat.apache.org/testsuite\">"));
}
@@ -979,46 +997,4 @@ public class TestWebdavServlet extends TomcatBaseTest {
}
}
- private static boolean proppatchSuccess = false;
-
- private class CustomWebdavServlet extends WebdavServlet {
-
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void proppatchResource(String path,
ArrayList<ProppatchOperation> operations) {
- for (ProppatchOperation operation : operations) {
- if (operation.getUpdateType().equals(PropertyUpdateType.SET)
- &&
operation.getPropertyNode().getLocalName().equals("customprop")) {
- proppatchSuccess = true;
- }
- operation.setStatusCode(HttpServletResponse.SC_OK);
- }
- }
-
- @Override
- protected boolean propfindResource(String path, org.w3c.dom.Node
property, boolean nameOnly, XMLWriter generatedXML) {
- if (nameOnly) {
- generatedXML.writeElement("T",
"http://tomcat.apache.org/testsuite", "customprop", XMLWriter.NO_CONTENT);
- generatedXML.writeElement("T",
"http://tomcat.apache.org/testsuite", "othercustomprop", XMLWriter.NO_CONTENT);
- } else if (property == null) {
- generatedXML.writeElement("T",
"http://tomcat.apache.org/testsuite", "customprop", XMLWriter.OPENING);
- generatedXML.writeElement("T", "myvalue",
XMLWriter.NO_CONTENT);
- generatedXML.writeElement("T", "customprop",
XMLWriter.CLOSING);
- generatedXML.writeElement("T",
"http://tomcat.apache.org/testsuite", "othercustomprop", XMLWriter.OPENING);
- generatedXML.writeElement("T", "myothervalue",
XMLWriter.NO_CONTENT);
- generatedXML.writeElement("T", "othercustomprop",
XMLWriter.CLOSING);
- } else if (property.getLocalName().equals("customprop")) {
- generatedXML.writeElement("T",
"http://tomcat.apache.org/testsuite", "customprop", XMLWriter.OPENING);
- generatedXML.writeElement("T", "myvalue",
XMLWriter.NO_CONTENT);
- generatedXML.writeElement("T", "customprop",
XMLWriter.CLOSING);
- return true;
- } else if (property.getLocalName().equals("othercustomprop")) {
- return false;
- }
- return false;
- }
-
- }
-
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]