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