Author: davsclaus Date: Mon Aug 13 16:06:44 2012 New Revision: 1372485 URL: http://svn.apache.org/viewvc?rev=1372485&view=rev Log: CAMEL-5501: Optimized XPathBuilder to cleanup thread locals after evaluation. Optimized performance under load by only using 1 thread local. Ensures better GC as well when using @XPath annotation.
Added: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanOgnMethodWithXPathInjectionTest.java - copied, changed from r1372409, camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionTest.java Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/builder/xml/XPathBuilder.java Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/builder/xml/XPathBuilder.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/builder/xml/XPathBuilder.java?rev=1372485&r1=1372484&r2=1372485&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/builder/xml/XPathBuilder.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/builder/xml/XPathBuilder.java Mon Aug 13 16:06:44 2012 @@ -92,8 +92,8 @@ public class XPathBuilder implements Exp private final Queue<XPathExpression> pool = new ConcurrentLinkedQueue<XPathExpression>(); private final Queue<XPathExpression> poolLogNamespaces = new ConcurrentLinkedQueue<XPathExpression>(); private final String text; - private final ThreadLocal<MessageVariableResolver> variableResolver = new ThreadLocal<MessageVariableResolver>(); private final ThreadLocal<Exchange> exchange = new ThreadLocal<Exchange>(); + private final MessageVariableResolver variableResolver = new MessageVariableResolver(exchange); private XPathFactory xpathFactory; private Class<?> documentType = Document.class; // For some reason the default expression of "a/b" on a document such as @@ -133,19 +133,23 @@ public class XPathBuilder implements Exp } public boolean matches(Exchange exchange) { - // add on completion so the thread locals is removed when exchange is done - exchange.addOnCompletion(new XPathBuilderOnCompletion()); - - Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN); - return exchange.getContext().getTypeConverter().convertTo(Boolean.class, booleanResult); + try { + Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN); + return exchange.getContext().getTypeConverter().convertTo(Boolean.class, booleanResult); + } finally { + // remove the thread local after usage + this.exchange.remove(); + } } public <T> T evaluate(Exchange exchange, Class<T> type) { - // add on completion so the thread locals is removed when exchange is done - exchange.addOnCompletion(new XPathBuilderOnCompletion()); - - Object result = evaluate(exchange); - return exchange.getContext().getTypeConverter().convertTo(type, result); + try { + Object result = evaluate(exchange); + return exchange.getContext().getTypeConverter().convertTo(type, result); + } finally { + // remove the thread local after usage + this.exchange.remove(); + } } /** @@ -165,8 +169,7 @@ public class XPathBuilder implements Exp try { return matches(dummy); } finally { - // remove the dummy from the thread local after usage - variableResolver.remove(); + // remove the thread local after usage exchange.remove(); } } @@ -189,8 +192,7 @@ public class XPathBuilder implements Exp try { return evaluate(dummy, type); } finally { - // remove the dummy from the thread local after usage - variableResolver.remove(); + // remove the thread local after usage exchange.remove(); } } @@ -213,9 +215,8 @@ public class XPathBuilder implements Exp try { return evaluate(dummy, String.class); } finally { - // remove the dummy from the thread local after usage - variableResolver.remove(); - exchange.remove(); + // remove the thread local after usage + this.exchange.remove(); } } @@ -1006,12 +1007,7 @@ public class XPathBuilder implements Exp } private MessageVariableResolver getVariableResolver() { - MessageVariableResolver resolver = variableResolver.get(); - if (resolver == null) { - resolver = new MessageVariableResolver(exchange); - variableResolver.set(resolver); - } - return resolver; + return variableResolver; } public void start() throws Exception { @@ -1052,33 +1048,4 @@ public class XPathBuilder implements Exp } } - /** - * On completion class which cleanup thread local resources - */ - private final class XPathBuilderOnCompletion extends SynchronizationAdapter { - - @Override - public void onDone(Exchange exchange) { - // when the exchange is done, then cleanup thread locals if they are still - // pointing to this exchange that was done - if (exchange.equals(XPathBuilder.this.exchange.get())) { - // cleanup thread locals after usage - XPathBuilder.this.variableResolver.remove(); - XPathBuilder.this.exchange.remove(); - } - } - - @Override - public boolean allowHandover() { - // this completion should not be handed over, as we want to execute it - // on current thread as the thread locals is bound the current thread - return false; - } - - @Override - public String toString() { - return "XPathBuilderOnCompletion"; - } - } - } Copied: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanOgnMethodWithXPathInjectionTest.java (from r1372409, camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionTest.java) URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanOgnMethodWithXPathInjectionTest.java?p2=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanOgnMethodWithXPathInjectionTest.java&p1=camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionTest.java&r1=1372409&r2=1372485&rev=1372485&view=diff ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanWithXPathInjectionTest.java (original) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/processor/BeanOgnMethodWithXPathInjectionTest.java Mon Aug 13 16:06:44 2012 @@ -28,15 +28,17 @@ import org.slf4j.LoggerFactory; /** * @version */ -public class BeanWithXPathInjectionTest extends ContextTestSupport { +public class BeanOgnMethodWithXPathInjectionTest extends ContextTestSupport { private static final transient Logger LOG = LoggerFactory.getLogger(BeanRouteTest.class); protected MyBean myBean = new MyBean(); + protected MyOtherBean myOtherBean = new MyOtherBean(myBean); public void testSendMessage() throws Exception { String expectedBody = "<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'><env:Body>" + "<foo>bar</foo></env:Body></env:Envelope>"; - template.sendBodyAndHeader("direct:in", expectedBody, "foo", "bar"); + Object out = template.requestBodyAndHeader("direct:in", expectedBody, "foo", "bar"); + assertEquals("bar", out); assertEquals("bean body: " + myBean, expectedBody, myBean.body); assertEquals("bean foo: " + myBean, "bar", myBean.foo); @@ -47,7 +49,8 @@ public class BeanWithXPathInjectionTest String expectedBody = "<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'><env:Body>" + "<foo>bar</foo></env:Body></env:Envelope>"; - template.sendBodyAndHeader("direct:in", expectedBody, "foo", "bar"); + Object out = template.requestBodyAndHeader("direct:in", expectedBody, "foo", "bar"); + assertEquals("bar", out); assertEquals("bean body: " + myBean, expectedBody, myBean.body); assertEquals("bean foo: " + myBean, "bar", myBean.foo); @@ -56,7 +59,8 @@ public class BeanWithXPathInjectionTest String expectedBody2 = "<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'><env:Body>" + "<foo>baz</foo></env:Body></env:Envelope>"; - template.sendBodyAndHeader("direct:in", expectedBody2, "foo", "baz"); + Object out2 = template.requestBodyAndHeader("direct:in", expectedBody2, "foo", "bar"); + assertEquals("baz", out2); assertEquals("bean body: " + myBean, expectedBody2, myBean.body); assertEquals("bean foo: " + myBean, "baz", myBean.foo); @@ -66,17 +70,33 @@ public class BeanWithXPathInjectionTest protected Context createJndiContext() throws Exception { JndiContext answer = new JndiContext(); answer.bind("myBean", myBean); + answer.bind("myOtherBean", myOtherBean); return answer; } protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { public void configure() { - from("direct:in").beanRef("myBean"); + from("direct:in") + .transform().method("myOtherBean", "doSomething.read"); } }; } + public static class MyOtherBean { + + private final MyBean inner; + + public MyOtherBean(MyBean inner) { + this.inner = inner; + } + + public MyBean doSomething() { + return inner; + } + + } + public static class MyBean { public String body; public String foo; @@ -86,10 +106,11 @@ public class BeanWithXPathInjectionTest return "MyBean[foo: " + foo + " body: " + body + "]"; } - public void read(String body, @XPath("/soap:Envelope/soap:Body/foo/text()") String foo) { + public String read(String body, @XPath("/soap:Envelope/soap:Body/foo/text()") String foo) { this.foo = foo; this.body = body; LOG.info("read() method called on " + this); + return foo; } } }