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 2009/05/18 16:00:54 UTC
svn commit: r775955 - in /camel/trunk/camel-core/src:
main/java/org/apache/camel/ main/java/org/apache/camel/component/bean/
test/java/org/apache/camel/component/bean/
Author: davsclaus
Date: Mon May 18 14:00:52 2009
New Revision: 775955
URL: http://svn.apache.org/viewvc?rev=775955&view=rev
Log:
CAMEL-1005: Added @Handler to help mark which method Camel should invoke for Bean Integration. To give more power to POJO routing as a liable DSL as well.
Added:
camel/trunk/camel-core/src/main/java/org/apache/camel/Handler.java
- copied, changed from r775808, camel/trunk/camel-core/src/main/java/org/apache/camel/Body.java
camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanHandlerMethodTest.java
- copied, changed from r775864, camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanExcludedMethodTest.java
Modified:
camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java
camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfo.java
camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoAMoreComplexOverloadedTest.java
camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoSelectMethodTest.java
camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ChainedBeanInvocationTest.java
camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ExpressionAnnotationToDisambiguateMethodsTest.java
Copied: camel/trunk/camel-core/src/main/java/org/apache/camel/Handler.java (from r775808, camel/trunk/camel-core/src/main/java/org/apache/camel/Body.java)
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/Handler.java?p2=camel/trunk/camel-core/src/main/java/org/apache/camel/Handler.java&p1=camel/trunk/camel-core/src/main/java/org/apache/camel/Body.java&r1=775808&r2=775955&rev=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/Body.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/Handler.java Mon May 18 14:00:52 2009
@@ -23,12 +23,13 @@
import java.lang.annotation.Target;
/**
- * Marks a parameter as being the body of an inbound {@link Message}
- *
+ * Marks a method on a POJO as being the preferred method to invoke when Camel looks
+ * for methods to invoke using the {@link org.apache.camel.component.bean.BeanEndpoint BeanEndpoint}.
+ *
* @version $Revision$
*/
@Retention(RetentionPolicy.RUNTIME)
@Documented
-@Target({ElementType.PARAMETER })
-public @interface Body {
-}
+@Target({ElementType.METHOD })
+public @interface Handler {
+}
\ No newline at end of file
Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java?rev=775955&r1=775954&r2=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java Mon May 18 14:00:52 2009
@@ -31,6 +31,7 @@
import org.apache.camel.Exchange;
import org.apache.camel.ExchangeException;
import org.apache.camel.Expression;
+import org.apache.camel.Handler;
import org.apache.camel.Header;
import org.apache.camel.Headers;
import org.apache.camel.Message;
@@ -63,6 +64,7 @@
private final Map<String, List<MethodInfo>> operations = new ConcurrentHashMap<String, List<MethodInfo>>();
private final List<MethodInfo> operationsWithBody = new ArrayList<MethodInfo>();
private final List<MethodInfo> operationsWithCustomAnnotation = new ArrayList<MethodInfo>();
+ private final List<MethodInfo> operationsWithHandlerAnnotation = new ArrayList<MethodInfo>();
private final Map<Method, MethodInfo> methodMap = new ConcurrentHashMap<Method, MethodInfo>();
private MethodInfo defaultMethod;
private BeanInfo superBeanInfo;
@@ -218,11 +220,14 @@
operations.put(opName, methods);
}
- if (methodInfo.hasBodyParameter()) {
+ if (methodInfo.hasCustomAnnotation()) {
+ operationsWithCustomAnnotation.add(methodInfo);
+ } else if (methodInfo.hasBodyParameter()) {
operationsWithBody.add(methodInfo);
}
- if (methodInfo.isHasCustomAnnotation() && !methodInfo.hasBodyParameter()) {
- operationsWithCustomAnnotation.add(methodInfo);
+
+ if (methodInfo.hasHandlerAnnotation()) {
+ operationsWithHandlerAnnotation.add(methodInfo);
}
// must add to method map last otherwise we break stuff
@@ -260,6 +265,7 @@
List<ParameterInfo> bodyParameters = new ArrayList<ParameterInfo>();
boolean hasCustomAnnotation = false;
+ boolean hasHandlerAnnotation = ObjectHelper.hasAnnotation(method.getAnnotations(), Handler.class);
for (int i = 0; i < parameterTypes.length; i++) {
Class parameterType = parameterTypes[i];
Annotation[] parameterAnnotations = parametersAnnotations[i];
@@ -289,8 +295,7 @@
}
// now lets add the method to the repository
- MethodInfo methodInfo = new MethodInfo(clazz, method, parameters, bodyParameters, hasCustomAnnotation);
- return methodInfo;
+ return new MethodInfo(clazz, method, parameters, bodyParameters, hasCustomAnnotation, hasHandlerAnnotation);
}
/**
@@ -299,27 +304,52 @@
*
* @param pojo the bean to invoke a method on
* @param exchange the message exchange
- * @return the method to invoke or null if no definitive method could be
- * matched
+ * @return the method to invoke or null if no definitive method could be matched
* @throws AmbiguousMethodCallException is thrown if cannot chose method due to ambiguous
*/
protected MethodInfo chooseMethod(Object pojo, Exchange exchange) throws AmbiguousMethodCallException {
- if (operationsWithBody.size() == 1) {
- // only one body possible so we got a hit
- return operationsWithBody.get(0);
- } else if (!operationsWithBody.isEmpty()) {
- // multiple operations so find the best suited if possible
- return chooseMethodWithMatchingBody(exchange, operationsWithBody);
+ // @Handler should be select first
+ // then any single method that has a custom @annotation
+ // or any single method that has a match parameter type that matches the Exchange payload
+ // and last then try to select the best among the rest
+
+ if (operationsWithHandlerAnnotation.size() > 1) {
+ // if we have more than 1 @Handler then its ambiguous
+ throw new AmbiguousMethodCallException(exchange, operationsWithHandlerAnnotation);
+ }
+
+ if (operationsWithHandlerAnnotation.size() == 1) {
+ // methods with handler should be preferred
+ return operationsWithHandlerAnnotation.get(0);
} else if (operationsWithCustomAnnotation.size() == 1) {
// if there is one method with an annotation then use that one
return operationsWithCustomAnnotation.get(0);
+ } else if (operationsWithBody.size() == 1) {
+ // if there is one method with body then use that one
+ return operationsWithBody.get(0);
+ }
+
+ Collection<MethodInfo> possibleOperations = new ArrayList<MethodInfo>();
+ possibleOperations.addAll(operationsWithBody);
+ possibleOperations.addAll(operationsWithCustomAnnotation);
+
+ if (!possibleOperations.isEmpty()) {
+ // multiple possible operations so find the best suited if possible
+ MethodInfo answer = chooseMethodWithMatchingBody(exchange, possibleOperations);
+ if (answer == null) {
+ throw new AmbiguousMethodCallException(exchange, possibleOperations);
+ } else {
+ return answer;
+ }
}
- // no we could not find a method to invoke, so either there are none or there are ambigiuous methods.
+
+ // not possible to determine
return null;
}
@SuppressWarnings("unchecked")
- private MethodInfo chooseMethodWithMatchingBody(Exchange exchange, Collection<MethodInfo> operationList) throws AmbiguousMethodCallException {
+ private MethodInfo chooseMethodWithMatchingBody(Exchange exchange, Collection<MethodInfo> operationList)
+ throws AmbiguousMethodCallException {
// lets see if we can find a method who's body param type matches the message body
Message in = exchange.getIn();
Object body = in.getBody();
@@ -478,11 +508,12 @@
return null;
}
- private MethodInfo chooseMethodWithCustomAnnotations(Exchange exchange, Collection<MethodInfo> possibles) throws AmbiguousMethodCallException {
+ private MethodInfo chooseMethodWithCustomAnnotations(Exchange exchange, Collection<MethodInfo> possibles)
+ throws AmbiguousMethodCallException {
// if we have only one method with custom annotations lets choose that
MethodInfo chosen = null;
for (MethodInfo possible : possibles) {
- if (possible.isHasCustomAnnotation()) {
+ if (possible.hasCustomAnnotation()) {
if (chosen != null) {
chosen = null;
break;
@@ -542,7 +573,7 @@
AnnotationExpressionFactory expressionFactory = (AnnotationExpressionFactory) object;
return expressionFactory.createExpression(camelContext, annotation, languageAnnotation, parameterType);
} else {
- LOG.error("Ignoring bad annotation: " + languageAnnotation + "on method: " + method
+ LOG.warn("Ignoring bad annotation: " + languageAnnotation + "on method: " + method
+ " which declares a factory: " + type.getName()
+ " which does not implement " + AnnotationExpressionFactory.class.getName());
}
Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfo.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfo.java?rev=775955&r1=775954&r2=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfo.java (original)
+++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/MethodInfo.java Mon May 18 14:00:52 2009
@@ -50,16 +50,19 @@
private final List<ParameterInfo> parameters;
private final List<ParameterInfo> bodyParameters;
private final boolean hasCustomAnnotation;
+ private final boolean hasHandlerAnnotation;
private Expression parametersExpression;
private ExchangePattern pattern = ExchangePattern.InOut;
private RecipientList recipientList;
- public MethodInfo(Class type, Method method, List<ParameterInfo> parameters, List<ParameterInfo> bodyParameters, boolean hasCustomAnnotation) {
+ public MethodInfo(Class type, Method method, List<ParameterInfo> parameters, List<ParameterInfo> bodyParameters,
+ boolean hasCustomAnnotation, boolean hasHandlerAnnotation) {
this.type = type;
this.method = method;
this.parameters = parameters;
this.bodyParameters = bodyParameters;
this.hasCustomAnnotation = hasCustomAnnotation;
+ this.hasHandlerAnnotation = hasHandlerAnnotation;
this.parametersExpression = createParametersExpression();
Pattern oneway = findOneWayAnnotation(method);
if (oneway != null) {
@@ -134,6 +137,9 @@
}
public Class getBodyParameterType() {
+ if (bodyParameters.isEmpty()) {
+ return null;
+ }
ParameterInfo parameterInfo = bodyParameters.get(0);
return parameterInfo.getType();
}
@@ -151,10 +157,14 @@
return !bodyParameters.isEmpty();
}
- public boolean isHasCustomAnnotation() {
+ public boolean hasCustomAnnotation() {
return hasCustomAnnotation;
}
+ public boolean hasHandlerAnnotation() {
+ return hasHandlerAnnotation;
+ }
+
public boolean isReturnTypeVoid() {
return method.getReturnType().getName().equals("void");
}
Copied: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanHandlerMethodTest.java (from r775864, camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanExcludedMethodTest.java)
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanHandlerMethodTest.java?p2=camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanHandlerMethodTest.java&p1=camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanExcludedMethodTest.java&r1=775864&r2=775955&rev=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanExcludedMethodTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanHandlerMethodTest.java Mon May 18 14:00:52 2009
@@ -16,25 +16,51 @@
*/
package org.apache.camel.component.bean;
+import org.apache.camel.Body;
import org.apache.camel.ContextTestSupport;
import org.apache.camel.Exchange;
+import org.apache.camel.Handler;
import org.apache.camel.impl.DefaultExchange;
/**
* @version $Revision$
*/
-public class BeanExcludedMethodTest extends ContextTestSupport {
+public class BeanHandlerMethodTest extends ContextTestSupport {
- public void testExcludedMethod() throws Exception {
+ public void testNoHandleMethod() throws Exception {
+ BeanInfo info = new BeanInfo(context, MyNoDummyBean.class);
+
+ Exchange exchange = new DefaultExchange(context);
+ MyNoDummyBean pojo = new MyNoDummyBean();
+ MethodInvocation mi = info.createInvocation(pojo, exchange);
+ assertNotNull(mi);
+ assertEquals("hello", mi.getMethod().getName());
+ }
+
+ public void testAmbigiousMethod() throws Exception {
+ BeanInfo info = new BeanInfo(context, MyAmbigiousBean.class);
+
+ Exchange exchange = new DefaultExchange(context);
+ MyAmbigiousBean pojo = new MyAmbigiousBean();
+ try {
+ MethodInvocation mi = info.createInvocation(pojo, exchange);
+ fail("Should have thrown an exception");
+ } catch (AmbiguousMethodCallException e) {
+ assertEquals(2, e.getMethods().size());
+ }
+ }
+
+ public void testHandleMethod() throws Exception {
BeanInfo info = new BeanInfo(context, MyDummyBean.class);
Exchange exchange = new DefaultExchange(context);
MyDummyBean pojo = new MyDummyBean();
MethodInvocation mi = info.createInvocation(pojo, exchange);
- assertNull("Should not be possible to find a suitable method", mi);
+ assertNotNull(mi);
+ assertEquals("hello", mi.getMethod().getName());
}
- public void testNotExcludedMethod() throws Exception {
+ public void testHandleAndBodyMethod() throws Exception {
BeanInfo info = new BeanInfo(context, MyOtherDummyBean.class);
Exchange exchange = new DefaultExchange(context);
@@ -44,36 +70,87 @@
assertEquals("hello", mi.getMethod().getName());
}
+ public void testHandleAmbigious() throws Exception {
+ BeanInfo info = new BeanInfo(context, MyReallyDummyBean.class);
+
+ Exchange exchange = new DefaultExchange(context);
+ MyReallyDummyBean pojo = new MyReallyDummyBean();
+ try {
+ MethodInvocation mi = info.createInvocation(pojo, exchange);
+ fail("Should throw exception");
+ } catch (AmbiguousMethodCallException e) {
+ assertEquals(2, e.getMethods().size());
+ }
+ }
+
+ public static class MyNoDummyBean {
+
+ public String hello(@Body String hi) {
+ return "Hello " + hi;
+ }
+
+ public String doCompute(String input) {
+ fail("Should not invoke me");
+ return null;
+ }
+
+ }
+
+ public static class MyAmbigiousBean {
+
+ public String hello(String hi) {
+ fail("Should not invoke me");
+ return "Hello " + hi;
+ }
+
+ public String doCompute(String input) {
+ fail("Should not invoke me");
+ return null;
+ }
+
+ }
+
public static class MyDummyBean {
- @Override
- public boolean equals(Object obj) {
- fail("Should not call equals");
- return true;
+ @Handler
+ public String hello(String hi) {
+ return "Hello " + hi;
}
- @Override
- public String toString() {
- return "dummy";
+ public String doCompute(String input) {
+ fail("Should not invoke me");
+ return null;
}
+
}
public static class MyOtherDummyBean {
- @Override
- public boolean equals(Object obj) {
- fail("Should not call equals");
- return true;
+ @Handler
+ public String hello(String hi) {
+ return "Hello " + hi;
}
- @Override
- public String toString() {
- return "dummy";
+ public String bye(@Body String input) {
+ fail("Should not invoke me");
+ return null;
}
+ }
+
+ public static class MyReallyDummyBean {
+
+ @Handler
public String hello(String hi) {
return "Hello " + hi;
}
+
+ @Handler
+ public String bye(@Body String input) {
+ fail("Should not invoke me");
+ return null;
+ }
+
}
-}
+}
\ No newline at end of file
Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoAMoreComplexOverloadedTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoAMoreComplexOverloadedTest.java?rev=775955&r1=775954&r2=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoAMoreComplexOverloadedTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoAMoreComplexOverloadedTest.java Mon May 18 14:00:52 2009
@@ -61,7 +61,7 @@
assertEquals(RequestB.class, method.getGenericParameterTypes()[0]);
}
- public void testNoMatch() throws Exception {
+ public void testAmbigious() throws Exception {
BeanInfo beanInfo = new BeanInfo(context, Bean.class);
Message message = new DefaultMessage();
@@ -69,8 +69,12 @@
Exchange exchange = new DefaultExchange(context);
exchange.setIn(message);
- MethodInvocation methodInvocation = beanInfo.createInvocation(new Bean(), exchange);
- assertNull("Should not find a suitable method", methodInvocation);
+ try {
+ MethodInvocation methodInvocation = beanInfo.createInvocation(new Bean(), exchange);
+ fail("Should have thrown an exception");
+ } catch (AmbiguousMethodCallException e) {
+ assertEquals(2, e.getMethods().size());
+ }
}
class Bean {
Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoSelectMethodTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoSelectMethodTest.java?rev=775955&r1=775954&r2=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoSelectMethodTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanInfoSelectMethodTest.java Mon May 18 14:00:52 2009
@@ -16,8 +16,10 @@
*/
package org.apache.camel.component.bean;
+import org.apache.camel.Body;
import org.apache.camel.ContextTestSupport;
import org.apache.camel.Exchange;
+import org.apache.camel.ExchangeException;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.JndiRegistry;
@@ -56,9 +58,9 @@
return new RouteBuilder() {
@Override
public void configure() throws Exception {
- errorHandler(deadLetterChannel().logStackTrace(false).disableRedelivery());
+ errorHandler(deadLetterChannel().logStackTrace(false).maximumRedeliveries(3));
- onException(Exception.class).handled(true).beanRef("foo").to("mock:result");
+ onException(Exception.class).handled(true).beanRef("foo", "handleFailure").to("mock:result");
from("direct:a").beanRef("foo").to("mock:result");
@@ -76,11 +78,11 @@
return "Exception";
}
- public String handleFailure(String order, Exception e) {
+ public String handleFailure(@Body String order, @ExchangeException IllegalArgumentException e) {
return "Failure";
}
- public String handleOrder(String order) {
+ public String handleOrder(@Body String order) {
return "Order";
}
}
Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ChainedBeanInvocationTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ChainedBeanInvocationTest.java?rev=775955&r1=775954&r2=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ChainedBeanInvocationTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ChainedBeanInvocationTest.java Mon May 18 14:00:52 2009
@@ -81,7 +81,7 @@
EasyMock.replay(beanMock);
Exchange result = template.send("direct:start2", new DefaultExchange(context));
assertNotNull(result.getException());
- assertEquals(result.getException().getClass(), IllegalStateException.class);
+ assertIsInstanceOf(AmbiguousMethodCallException.class, result.getException());
EasyMock.verify(beanMock);
}
Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ExpressionAnnotationToDisambiguateMethodsTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ExpressionAnnotationToDisambiguateMethodsTest.java?rev=775955&r1=775954&r2=775955&view=diff
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ExpressionAnnotationToDisambiguateMethodsTest.java (original)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/ExpressionAnnotationToDisambiguateMethodsTest.java Mon May 18 14:00:52 2009
@@ -19,6 +19,7 @@
import javax.naming.Context;
import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Handler;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.language.Simple;
import org.apache.camel.processor.BeanRouteTest;
@@ -32,6 +33,7 @@
public class ExpressionAnnotationToDisambiguateMethodsTest extends ContextTestSupport {
private static final transient Log LOG = LogFactory.getLog(BeanRouteTest.class);
protected MyBean myBean = new MyBean();
+ protected MyOtherBean myOtherBean = new MyOtherBean();
public void testSendMessage() throws Exception {
template.sendBodyAndHeader("direct:in", "<hello>world!</hello>", "foo", "bar");
@@ -39,10 +41,17 @@
assertEquals("bean body: " + myBean, "bar", myBean.bar);
}
+ public void testSendMessageHandler() throws Exception {
+ template.sendBodyAndHeader("direct:other", "<hello>world!</hello>", "foo", "bar");
+
+ assertEquals("bean body: " + myOtherBean, "bar", myOtherBean.bar);
+ }
+
@Override
protected Context createJndiContext() throws Exception {
JndiContext answer = new JndiContext();
answer.bind("myBean", myBean);
+ answer.bind("myOtherBean", myOtherBean);
return answer;
}
@@ -50,6 +59,8 @@
return new RouteBuilder() {
public void configure() {
from("direct:in").beanRef("myBean");
+
+ from("direct:other").beanRef("myOtherBean");
}
};
}
@@ -70,4 +81,22 @@
fail("wrongMethod() called with: " + body);
}
}
+
+ public static class MyOtherBean {
+ public String bar;
+
+ public void bar(String body) {
+ fail("bar() called with: " + body);
+ }
+
+ @Handler
+ public void foo(@Simple("header.foo") String bar) {
+ this.bar = bar;
+ LOG.info("foo() method called with: " + bar);
+ }
+
+ public void wrongMethod(String body) {
+ fail("wrongMethod() called with: " + body);
+ }
+ }
}
\ No newline at end of file