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 {