Author: davsclaus
Date: Fri Feb 27 08:53:05 2009
New Revision: 748436

URL: http://svn.apache.org/viewvc?rev=748436&view=rev
Log:
CAMEL-1401: Fixed thread safe issue with JAXBDataFormat.

Added:
    
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java
   (with props)
    
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java
   (contents, props changed)
      - copied, changed from r748394, 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatTest.java
Modified:
    
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java
    
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbConverter.java
    
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
    
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/JAXBConvertTest.java

Modified: 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java?rev=748436&r1=748435&r2=748436&view=diff
==============================================================================
--- 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java
 (original)
+++ 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/FallbackTypeConverter.java
 Fri Feb 27 08:53:05 2009
@@ -21,6 +21,8 @@
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
@@ -44,6 +46,7 @@
  */
 public class FallbackTypeConverter implements TypeConverter, 
TypeConverterAware {
     private static final transient Log LOG = 
LogFactory.getLog(FallbackTypeConverter.class);
+    private Map<Class, JAXBContext> contexts = new HashMap<Class, 
JAXBContext>();
     private TypeConverter parentTypeConverter;
     private boolean prettyPrint = true;
 
@@ -85,15 +88,19 @@
 
     protected <T> boolean isJaxbType(Class<T> type) {
         XmlRootElement element = type.getAnnotation(XmlRootElement.class);
-        boolean jaxbType = element != null;
-        return jaxbType;
+        return element != null;
     }
 
     /**
      * Lets try parse via JAXB
      */
     protected <T> T unmarshall(Class<T> type, Object value) throws 
JAXBException {
+        if (value == null) {
+            throw new IllegalArgumentException("Cannot convert from null value 
to JAXBSource");
+        }
+
         JAXBContext context = createContext(type);
+        // must create a new instance of unmarshaller as its not thred safe
         Unmarshaller unmarshaller = context.createUnmarshaller();
 
         if (parentTypeConverter != null) {
@@ -136,6 +143,7 @@
             } catch (NoTypeConversionAvailableException e) {
                 // lets try a stream
                 StringWriter buffer = new StringWriter();
+                // must create a new instance of marshaller as its not thred 
safe
                 Marshaller marshaller = context.createMarshaller();
                 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 
isPrettyPrint() ? Boolean.TRUE : Boolean.FALSE);
                 marshaller.marshal(value, buffer);
@@ -172,8 +180,13 @@
         return null;
     }
 
-    protected <T> JAXBContext createContext(Class<T> type) throws 
JAXBException {
-        JAXBContext context = JAXBContext.newInstance(type);
+    protected synchronized <T> JAXBContext createContext(Class<T> type) throws 
JAXBException {
+        JAXBContext context = contexts.get(type);
+        if (context == null) {
+            context = JAXBContext.newInstance(type);
+            contexts.put(type, context);
+        }
         return context;
     }
+
 }

Modified: 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbConverter.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbConverter.java?rev=748436&r1=748435&r2=748436&view=diff
==============================================================================
--- 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbConverter.java
 (original)
+++ 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbConverter.java
 Fri Feb 27 08:53:05 2009
@@ -25,44 +25,39 @@
 import javax.xml.bind.util.JAXBSource;
 import javax.xml.parsers.ParserConfigurationException;
 
-import org.w3c.dom.Document;
-
 import org.apache.camel.Converter;
 import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.converter.HasAnnotation;
 import org.apache.camel.converter.jaxp.XmlConverter;
+import org.w3c.dom.Document;
 
 /**
  * @version $Revision$
  */
 public final class JaxbConverter {
-    private XmlConverter jaxbConverter;
+    private XmlConverter xmlConverter = new XmlConverter();
     private Map<Class, JAXBContext> contexts = new HashMap<Class, 
JAXBContext>();
 
-    public XmlConverter getJaxbConverter() {
-        if (jaxbConverter == null) {
-            jaxbConverter = new XmlConverter();
-        }
-        return jaxbConverter;
-    }
-
-    public void setJaxbConverter(XmlConverter jaxbConverter) {
-        this.jaxbConverter = jaxbConverter;
-    }
-
     @Converter
     public JAXBSource toSource(@HasAnnotation(XmlRootElement.class)Object 
value) throws JAXBException {
+        if (value == null) {
+            throw new IllegalArgumentException("Cannot convert from null value 
to JAXBSource");
+        }
         JAXBContext context = getJaxbContext(value);
         return new JAXBSource(context, value);
     }
 
     @Converter
     public Document toDocument(@HasAnnotation(XmlRootElement.class)Object 
value) throws JAXBException, ParserConfigurationException {
+        if (value == null) {
+            throw new IllegalArgumentException("Cannot convert from null value 
to JAXBSource");
+        }
         JAXBContext context = getJaxbContext(value);
+        // must create a new instance of marshaller as its not thred safe
         Marshaller marshaller = context.createMarshaller();
 
-        Document doc = getJaxbConverter().createDocument();
+        Document doc = xmlConverter.createDocument();
         marshaller.marshal(value, doc);
         return doc;
     }
@@ -80,19 +75,13 @@
     }
 
     private synchronized JAXBContext getJaxbContext(Object value) throws 
JAXBException {
-        JAXBContext context = contexts.get(value.getClass());
+        Class type = value.getClass();
+        JAXBContext context = contexts.get(type);
         if (context == null) {
-            context = createJaxbContext(value);
-            contexts.put(value.getClass(), context);
+            context = JAXBContext.newInstance(type);
+            contexts.put(type, context);
         }
         return context;
     }
 
-    private JAXBContext createJaxbContext(Object value) throws JAXBException {
-        if (value == null) {
-            throw new IllegalArgumentException("Cannot convert from null value 
to JAXBSource");
-        }
-        return JAXBContext.newInstance(value.getClass());
-    }
-
 }

Modified: 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java?rev=748436&r1=748435&r2=748436&view=diff
==============================================================================
--- 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
 (original)
+++ 
camel/trunk/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java
 Fri Feb 27 08:53:05 2009
@@ -19,12 +19,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.JAXBException;
-import javax.xml.bind.Marshaller;
-import javax.xml.bind.Unmarshaller;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.spi.DataFormat;
@@ -41,8 +38,6 @@
     private String contextPath;
     private boolean prettyPrint = true;
     private boolean ignoreJAXBElement = true;
-    private Marshaller marshaller;
-    private Unmarshaller unmarshaller;
 
     public JaxbDataFormat() {
     }
@@ -57,7 +52,8 @@
 
     public void marshal(Exchange exchange, Object graph, OutputStream stream) 
throws IOException {
         try {
-            getMarshaller().marshal(graph, stream);
+            // must create a new instance of marshaller as its not thred safe
+            getContext().createMarshaller().marshal(graph, stream);
         } catch (JAXBException e) {
             throw IOHelper.createIOException(e);
         }
@@ -65,7 +61,8 @@
 
     public Object unmarshal(Exchange exchange, InputStream stream) throws 
IOException, ClassNotFoundException {
         try {
-            Object answer = getUnmarshaller().unmarshal(stream);
+            // must create a new instance of unmarshaller as its not thred safe
+            Object answer = 
getContext().createUnmarshaller().unmarshal(stream);
             if (answer instanceof JAXBElement && isIgnoreJAXBElement()) {
                 answer = ((JAXBElement)answer).getValue();
             }
@@ -85,7 +82,7 @@
         ignoreJAXBElement = flag;
     }
     
-    public JAXBContext getContext() throws JAXBException {
+    public synchronized JAXBContext getContext() throws JAXBException {
         if (context == null) {
             context = createContext();
         }
@@ -104,17 +101,6 @@
         this.contextPath = contextPath;
     }
 
-    public Marshaller getMarshaller() throws JAXBException {
-        if (marshaller == null) {
-            marshaller = getContext().createMarshaller();
-        }
-        return marshaller;
-    }
-
-    public void setMarshaller(Marshaller marshaller) {
-        this.marshaller = marshaller;
-    }
-
     public boolean isPrettyPrint() {
         return prettyPrint;
     }
@@ -123,17 +109,6 @@
         this.prettyPrint = prettyPrint;
     }
 
-    public Unmarshaller getUnmarshaller() throws JAXBException {
-        if (unmarshaller == null) {
-            unmarshaller = getContext().createUnmarshaller();
-        }
-        return unmarshaller;
-    }
-
-    public void setUnmarshaller(Unmarshaller unmarshaller) {
-        this.unmarshaller = unmarshaller;
-    }
-
     protected JAXBContext createContext() throws JAXBException {
         if (contextPath != null) {
             return JAXBContext.newInstance(contextPath);

Added: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java?rev=748436&view=auto
==============================================================================
--- 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java
 (added)
+++ 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java
 Fri Feb 27 08:53:05 2009
@@ -0,0 +1,78 @@
+/**
+ * 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.camel.example;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.converter.jaxb.JaxbDataFormat;
+import org.apache.camel.spi.DataFormat;
+
+/**
+ * @version $Revision$
+ */
+public class DataFormatConcurrentTest extends ContextTestSupport {
+
+    private int size = 2000;
+
+    public void testSendConcurrent() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(size);
+
+        // wait for seda consumer to start up properly
+        Thread.sleep(1000);
+
+        ExecutorService executor = Executors.newCachedThreadPool();
+        for (int i = 0; i < size; i++) {
+
+            // sleep a little so we interleave with the marshaller
+            try {
+                Thread.sleep(1, 500);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+
+            executor.execute(new Runnable() {
+                public void run() {
+                    PurchaseOrder bean = new PurchaseOrder();
+                    bean.setName("Beer");
+                    bean.setAmount(23);
+                    bean.setPrice(2.5);
+
+                    template.sendBody("seda:start?size=" + size + 
"&concurrentConsumers=5", bean);
+                }
+            });
+        }
+
+        assertMockEndpointsSatisfied();
+    }
+
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                DataFormat jaxb = new 
JaxbDataFormat("org.apache.camel.example");
+
+                // use seda that supports concurrent consumers for concurrency
+                from("seda:start?size=" + size + 
"&concurrentConsumers=5").marshal(jaxb).convertBodyTo(String.class).to("mock:result");
+            }
+        };
+    }
+
+}
\ No newline at end of file

Propchange: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatConcurrentTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Copied: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java
 (from r748394, 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatTest.java)
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java?p2=camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java&p1=camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatTest.java&r1=748394&r2=748436&rev=748436&view=diff
==============================================================================
--- 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatTest.java
 (original)
+++ 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java
 Fri Feb 27 08:53:05 2009
@@ -16,45 +16,46 @@
  */
 package org.apache.camel.example;
 
+import javax.naming.Context;
+
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.dataset.SimpleDataSet;
 import org.apache.camel.converter.jaxb.JaxbDataFormat;
 import org.apache.camel.spi.DataFormat;
 
 /**
  * @version $Revision$
  */
-public class DataFormatTest extends ContextTestSupport {
+public class DataFormatDataSetTest extends ContextTestSupport {
+
+    public void testConcurrentMarshall() throws Exception {
+        assertMockEndpointsSatisfied();
+    }
 
-    public void testMarshalThenUnmarshalBean() throws Exception {
+    @Override
+    protected Context createJndiContext() throws Exception {
         PurchaseOrder bean = new PurchaseOrder();
         bean.setName("Beer");
         bean.setAmount(23);
         bean.setPrice(2.5);
 
-        MockEndpoint resultEndpoint = resolveMandatoryEndpoint("mock:result", 
MockEndpoint.class);
-        resultEndpoint.expectedBodiesReceived(bean);
-
-        template.sendBody("direct:start", bean);
-
-        resultEndpoint.assertIsSatisfied();
+        SimpleDataSet ds = new SimpleDataSet();
+        ds.setDefaultBody(bean);
+        ds.setSize(200);
+
+        Context context = super.createJndiContext();
+        context.bind("beer", ds);
+        return context;
     }
 
-
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
             public void configure() {
-
                 DataFormat jaxb = new 
JaxbDataFormat("org.apache.camel.example");
 
-                from("direct:start").
-                        marshal(jaxb).
-                        to("direct:marshalled");
-
-                from("direct:marshalled").
-                        unmarshal(jaxb).
-                        to("mock:result");
+                // use 5 concurrent threads to do marshalling
+                
from("dataset:beer").thread(5).marshal(jaxb).to("dataset:beer");
             }
         };
     }

Propchange: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/DataFormatDataSetTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/JAXBConvertTest.java
URL: 
http://svn.apache.org/viewvc/camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/JAXBConvertTest.java?rev=748436&r1=748435&r2=748436&view=diff
==============================================================================
--- 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/JAXBConvertTest.java
 (original)
+++ 
camel/trunk/components/camel-jaxb/src/test/java/org/apache/camel/example/JAXBConvertTest.java
 Fri Feb 27 08:53:05 2009
@@ -43,6 +43,25 @@
         assertNotNull("Purchase order should not be null!", purchaseOrder);
         assertEquals("name", "foo", purchaseOrder.getName());
         assertEquals("amount", 123.45, purchaseOrder.getAmount());
+        assertEquals("price", 2.22, purchaseOrder.getPrice());
+    }
+
+    public void testConverterTwice() throws Exception {
+        PurchaseOrder purchaseOrder = converter.convertTo(PurchaseOrder.class,
+            "<purchaseOrder name='foo' amount='123.45' price='2.22'/>");
+
+        assertNotNull("Purchase order should not be null!", purchaseOrder);
+        assertEquals("name", "foo", purchaseOrder.getName());
+        assertEquals("amount", 123.45, purchaseOrder.getAmount());
+        assertEquals("price", 2.22, purchaseOrder.getPrice());
+
+        PurchaseOrder purchaseOrder2 = converter.convertTo(PurchaseOrder.class,
+            "<purchaseOrder name='bar' amount='5.12' price='3.33'/>");
+
+        assertNotNull("Purchase order should not be null!", purchaseOrder2);
+        assertEquals("name", "bar", purchaseOrder2.getName());
+        assertEquals("amount", 5.12, purchaseOrder2.getAmount());
+        assertEquals("amount", 3.33, purchaseOrder2.getPrice());
     }
 
     public void testStreamShouldBeClosed() throws Exception {


Reply via email to