You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2010/05/11 09:13:38 UTC

svn commit: r943024 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/converter/jaxp/XmlConverter.java test/java/org/apache/camel/builder/xml/XPathTest.java test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java

Author: davsclaus
Date: Tue May 11 07:13:38 2010
New Revision: 943024

URL: http://svn.apache.org/viewvc?rev=943024&view=rev
Log:
CAMEL-2692: Fixed thread safety issue when converting Node to Dom.

Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathTest.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java

Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java?rev=943024&r1=943023&r2=943024&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/converter/jaxp/XmlConverter.java Tue May 11 07:13:38 2010
@@ -384,9 +384,10 @@ public class XmlConverter {
 
     @Converter
     public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException {
-        DocumentBuilder builder = createDocumentBuilder();
+        Document document;
         String systemId = source.getSystemId();
-        Document document = null;
+
+        DocumentBuilder builder = createDocumentBuilder();
         Reader reader = source.getReader();
         if (reader != null) {
             document = builder.parse(new InputSource(reader));
@@ -397,7 +398,7 @@ public class XmlConverter {
                 inputsource.setSystemId(systemId);
                 document = builder.parse(inputsource);
             } else {
-                throw new IOException("No input stream or reader available");
+                throw new IOException("No input stream or reader available on StreamSource: " + source);
             }
         }
         return new DOMSource(document, systemId);
@@ -550,13 +551,14 @@ public class XmlConverter {
 
     /**
      * Create a DOM document from the given Node.
-     * If the node is an document, just cast it,
-     * if the node is an root element, retrieve its
-     * owner element or create a new document and import
-     * the node.
+     *
+     * If the node is an document, just cast it, if the node is an root element, retrieve its
+     * owner element or create a new document and import the node.
      */
     @Converter
-    public Document toDOMDocument(Node node) throws ParserConfigurationException, TransformerException {
+    public Document toDOMDocument(final Node node) throws ParserConfigurationException, TransformerException {
+        ObjectHelper.notNull(node, "node");
+
         // If the node is the document, just cast it
         if (node instanceof Document) {
             return (Document) node;
@@ -569,23 +571,27 @@ public class XmlConverter {
                 // else, create a new doc and copy the element inside it
             } else {
                 Document doc = createDocument();
-                doc.appendChild(doc.importNode(node, true));
+                // import node must no occur concurrent on the same node
+                // so we need to synchronize on it
+                synchronized (node) {
+                    doc.appendChild(doc.importNode(node, true));
+                }
                 return doc;
             }
             // other element types are not handled
         } else {
-            throw new TransformerException("Unable to convert DOM node to a Document");
+            throw new TransformerException("Unable to convert DOM node to a Document: " + node);
         }
     }
 
     @Converter
-    public InputStream toInputStrean(DOMSource source) throws TransformerException, IOException {
+    public InputStream toInputStream(DOMSource source) throws TransformerException, IOException {
         String s = toString(source);
         return new ByteArrayInputStream(s.getBytes());
     }
 
     @Converter
-    public InputStream toInputStrean(Document dom) throws TransformerException, IOException {
+    public InputStream toInputStream(Document dom) throws TransformerException, IOException {
         String s = toString(dom);
         return new ByteArrayInputStream(s.getBytes());
     }

Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathTest.java?rev=943024&r1=943023&r2=943024&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/builder/xml/XPathTest.java Tue May 11 07:13:38 2010
@@ -17,20 +17,29 @@
 package org.apache.camel.builder.xml;
 
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFunctionResolver;
 
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
 import org.apache.camel.Predicate;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
 
 import static org.apache.camel.builder.xml.XPathBuilder.xpath;
 
@@ -313,4 +322,51 @@ public class XPathTest extends ContextTe
         assertEquals(true, bool.booleanValue());
     }
 
+    public void testXPathSplit() throws Exception {
+        Object node = XPathBuilder.xpath("foo/bar").nodeResult()
+                .evaluate(createExchange("<foo><bar>cheese</bar><bar>cake</bar><bar>beer</bar></foo>"));
+        assertNotNull(node);
+
+        Document doc = context.getTypeConverter().convertTo(Document.class, node);
+        assertNotNull(doc);
+    }
+
+    public void testXPathSplitConcurrent() throws Exception {
+        int size = 100;
+
+        final Object node = XPathBuilder.xpath("foo/bar").nodeResult()
+                .evaluate(createExchange("<foo><bar>cheese</bar><bar>cake</bar><bar>beer</bar></foo>"));
+        assertNotNull(node);
+
+        // convert the node concurrently to test that XML Parser is not thread safe when
+        // importing nodes to a new Document, so try a test for that
+
+        final Set<Document> result = new HashSet<Document>();
+        ExecutorService executor = Executors.newFixedThreadPool(size);
+        final CountDownLatch latch = new CountDownLatch(size);
+        for (int i = 0; i < size; i++) {
+            executor.submit(new Callable<Document>() {
+                public Document call() throws Exception {
+                    Document doc = context.getTypeConverter().convertTo(Document.class, node);
+                    result.add(doc);
+                    latch.countDown();
+                    return doc;
+                }
+            });
+        }
+
+        // give time to convert concurrently
+        latch.await(20, TimeUnit.SECONDS);
+
+        assertEquals(size, result.size());
+        Iterator<Document> it = result.iterator();
+        int count = 0;
+        while (it.hasNext()) {
+            count++;
+            Document doc = it.next();
+            assertNotNull(doc);
+        }
+        assertEquals(size, count);
+    }
+
 }

Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java?rev=943024&r1=943023&r2=943024&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/converter/jaxp/XmlConverterTest.java Tue May 11 07:13:38 2010
@@ -346,7 +346,7 @@ public class XmlConverterTest extends Co
         XmlConverter conv = new XmlConverter();
         Document doc = context.getTypeConverter().convertTo(Document.class, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo>bar</foo>");
 
-        InputStream is = conv.toInputStrean(doc);
+        InputStream is = conv.toInputStream(doc);
         assertNotNull(is);
         assertEquals("<foo>bar</foo>", context.getTypeConverter().convertTo(String.class, is));
     }
@@ -365,7 +365,7 @@ public class XmlConverterTest extends Co
         XmlConverter conv = new XmlConverter();
 
         DOMSource source = conv.toDOMSource("<foo>bar</foo>");
-        InputStream out = conv.toInputStrean(source);
+        InputStream out = conv.toInputStream(source);
         assertNotSame(source, out);
 
         String s = context.getTypeConverter().convertTo(String.class, out);