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 2011/03/25 16:06:27 UTC

svn commit: r1085420 - in /camel/trunk/camel-core/src: main/java/org/apache/camel/component/bean/ test/java/org/apache/camel/component/bean/

Author: davsclaus
Date: Fri Mar 25 15:06:26 2011
New Revision: 1085420

URL: http://svn.apache.org/viewvc?rev=1085420&view=rev
Log:
CAMEL-3515: Added support for using qualifier for selecting bean method to be used to pinpoint methods when having overloaded methods on beans.

Added:
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodFQNTest.java
    camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java
Modified:
    camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanInfo.java

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=1085420&r1=1085419&r2=1085420&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 Fri Mar 25 15:06:26 2011
@@ -140,28 +140,36 @@ public class BeanInfo {
         return null;
     }
 
-    public MethodInvocation createInvocation(Object pojo, Exchange exchange) throws AmbiguousMethodCallException, MethodNotFoundException {
+    public MethodInvocation createInvocation(Object pojo, Exchange exchange)
+        throws AmbiguousMethodCallException, MethodNotFoundException, ClassNotFoundException {
         MethodInfo methodInfo = null;
 
-        String name = exchange.getIn().getHeader(Exchange.BEAN_METHOD_NAME, String.class);
-        if (name != null) {
-            if (hasMethod(name)) {
-                List<MethodInfo> methods = getOperations(name);
-                if (methods != null && methods.size() == 1) {
-                    // only one method then choose it
-                    methodInfo = methods.get(0);
-                } else {
-                    // there are more methods with that name so we cannot decide which to use
+        String methodName = exchange.getIn().getHeader(Exchange.BEAN_METHOD_NAME, String.class);
+        if (methodName != null) {
 
-                    // but first lets try to choose a method and see if that comply with the name
-                    methodInfo = chooseMethod(pojo, exchange, name);
-                    if (methodInfo == null || !name.equals(methodInfo.getMethod().getName())) {
-                        throw new AmbiguousMethodCallException(exchange, methods);
-                    }
+            // do not use qualifier for name
+            String name = methodName;
+            if (methodName.contains("(")) {
+                name = ObjectHelper.before(methodName, "(");
+            }
+
+            List<MethodInfo> methods = getOperations(name);
+            if (methods != null && methods.size() == 1) {
+                // only one method then choose it
+                methodInfo = methods.get(0);
+            } else if (methods != null) {
+                // there are more methods with that name so we cannot decide which to use
+
+                // but first lets try to choose a method and see if that comply with the name
+                // must use the method name which may have qualifiers
+                methodInfo = chooseMethod(pojo, exchange, methodName);
+
+                if (methodInfo == null || !name.equals(methodInfo.getMethod().getName())) {
+                    throw new AmbiguousMethodCallException(exchange, methods);
                 }
             } else {
                 // a specific method was given to invoke but not found
-                throw new MethodNotFoundException(exchange, pojo, name);
+                throw new MethodNotFoundException(exchange, pojo, methodName);
             }
         }
         if (methodInfo == null) {
@@ -363,8 +371,9 @@ public class BeanInfo {
      * @param name an optional name of the method that must match, use <tt>null</tt> to indicate all methods
      * @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
+     * @throws ClassNotFoundException is thrown if name contains parameter types to use as qualifier and a class was not found
      */
-    protected MethodInfo chooseMethod(Object pojo, Exchange exchange, String name) throws AmbiguousMethodCallException {
+    protected MethodInfo chooseMethod(Object pojo, Exchange exchange, String name) throws AmbiguousMethodCallException, ClassNotFoundException {
         // @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
@@ -675,17 +684,76 @@ public class BeanInfo {
         }
     }
 
-    private static void removeNonMatchingMethods(List<MethodInfo> methods, String name) {
+    private void removeNonMatchingMethods(List<MethodInfo> methods, String name) throws ClassNotFoundException {
         Iterator<MethodInfo> it = methods.iterator();
         while (it.hasNext()) {
             MethodInfo info = it.next();
-            if (!name.equals(info.getMethod().getName())) {
+            if (!matchMethod(info.getMethod(), name)) {
                 // name does not match so remove it
                 it.remove();
             }
         }
     }
 
+    private boolean matchMethod(Method method, String methodName) throws ClassNotFoundException {
+        if (methodName == null) {
+            return true;
+        }
+
+        if (methodName.contains("(") && !methodName.endsWith(")")) {
+            throw new IllegalArgumentException("Name must have both starting and ending parenthesis, was: " + methodName);
+        }
+
+        // do not use qualifier for name matching
+        String name = methodName;
+        if (name.contains("(")) {
+            name = ObjectHelper.before(name, "(");
+        }
+
+        // must match name
+        if (!name.equals(method.getName())) {
+            return false;
+        }
+
+        // match qualifier types which is used to select among overloaded methods
+        String types = ObjectHelper.between(methodName, "(", ")");
+        if (types != null) {
+            // we must qualify based on types to match method
+            Iterator it = ObjectHelper.createIterator(types);
+            for (int i = 0; i < method.getParameterTypes().length; i++) {
+                if (it.hasNext()) {
+                    String qualifyType = (String) it.next();
+                    if ("*".equals(qualifyType)) {
+                        // * is a wildcard so we accept and match that parameter type
+                        continue;
+                    }
+
+                    // match on either simple name or FQN decided by end user as how
+                    // he specified the qualify type
+                    String parameterType = method.getParameterTypes()[i].getSimpleName();
+                    if (qualifyType.indexOf(".") > -1) {
+                        parameterType = method.getParameterTypes()[i].getName();
+                    }
+                    if (!parameterType.equals(qualifyType)) {
+                        return false;
+                    }
+                } else {
+                    // there method has more parameters than was specified in the method name qualifiers
+                    return false;
+                }
+            }
+
+            // if the method has no more types then we can only regard it as matched
+            // if there are no more qualifiers
+            if (it.hasNext()) {
+                return false;
+            }
+        }
+
+        // the method matched
+        return true;
+    }
+
     private static Class<?> getTargetClass(Class<?> clazz) {
         if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
             Class<?> superClass = clazz.getSuperclass();
@@ -744,6 +812,11 @@ public class BeanInfo {
      * @return the found method, or <tt>null</tt> if not found
      */
     private List<MethodInfo> getOperations(String methodName) {
+        // do not use qualifier for name
+        if (methodName.contains("(")) {
+            methodName = ObjectHelper.before(methodName, "(");
+        }
+
         List<MethodInfo> answer = operations.get(methodName);
         if (answer != null) {
             return answer;

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodFQNTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodFQNTest.java?rev=1085420&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodFQNTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodFQNTest.java Fri Mar 25 15:06:26 2011
@@ -0,0 +1,164 @@
+/**
+ * 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.component.bean;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+
+/**
+ *
+ */
+public class BeanOverloadedMethodFQNTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    public void testOrderNoFQN() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "order(MyOrder)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("OK");
+
+        template.sendBody("direct:start", new MyOrder());
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOrderNoFQNUnknown() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "order(Unknown)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        try {
+            template.sendBody("direct:start", new MyOrder());
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            AmbiguousMethodCallException cause = assertIsInstanceOf(AmbiguousMethodCallException.class, e.getCause());
+            assertEquals(2, cause.getMethods().size());
+        }
+    }
+
+    public void testOrderNoFQNBoolean() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "order(MyOrder,Boolean)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("OK;GOLD");
+
+        template.sendBody("direct:start", new MyOrder());
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOrderFQN() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "order(org.apache.camel.component.bean.BeanOverloadedMethodFQNTest$MyOrder)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("OK");
+
+        template.sendBody("direct:start", new MyOrder());
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testOrderFQNUnknown() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "order(org.apache.camel.component.bean.BeanOverloadedMethodFQNTest$Unknown)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        try {
+            template.sendBody("direct:start", new MyOrder());
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            AmbiguousMethodCallException cause = assertIsInstanceOf(AmbiguousMethodCallException.class, e.getCause());
+            assertEquals(2, cause.getMethods().size());
+        }
+    }
+
+    public void testOrderFQNBoolean() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "order(org.apache.camel.component.bean.BeanOverloadedMethodFQNTest$MyOrder,Boolean)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("OK;GOLD");
+
+        template.sendBody("direct:start", new MyOrder());
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public static final class MyOrder {
+    }
+
+    public static final class MyBean {
+
+        public String order(MyOrder order) {
+            return "OK";
+        }
+
+        public String order(MyOrder order, Boolean gold) {
+            return "OK;GOLD";
+        }
+    }
+}

Added: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java
URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java?rev=1085420&view=auto
==============================================================================
--- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java (added)
+++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java Fri Mar 25 15:06:26 2011
@@ -0,0 +1,272 @@
+/**
+ * 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.component.bean;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Header;
+import org.apache.camel.builder.RouteBuilder;
+
+/**
+ *
+ */
+public class BeanOverloadedMethodTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    public void testHelloOverloadedHeString() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(String)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello Claus");
+
+        template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testHelloOverloadedWildcard() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(*)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello Claus");
+
+        template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testHelloOverloadedStringString() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(String,String)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello Claus you are from Denmark");
+
+        template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testHelloOverloadedWildcardString() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(*,String)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello Claus you are from Denmark");
+
+        template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testHelloOverloadedPickCamelAnnotated() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello Claus you are from Denmark");
+
+        template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testHelloOverloadedAmbiguousStringStringString() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(String,String,String)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        try {
+            template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            AmbiguousMethodCallException cause = assertIsInstanceOf(AmbiguousMethodCallException.class, e.getCause());
+            assertEquals(2, cause.getMethods().size());
+        }
+    }
+
+    public void testHelloOverloadedStringInt() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(String,int)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        try {
+            template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            AmbiguousMethodCallException cause = assertIsInstanceOf(AmbiguousMethodCallException.class, e.getCause());
+            assertEquals(2, cause.getMethods().size());
+        }
+    }
+
+    public void testHelloOverloadedIntString() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "hello(int,String)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        try {
+            template.sendBodyAndHeader("direct:start", "Claus", "country", "Denmark");
+            fail("Should have thrown an exception");
+        } catch (CamelExecutionException e) {
+            AmbiguousMethodCallException cause = assertIsInstanceOf(AmbiguousMethodCallException.class, e.getCause());
+            assertEquals(2, cause.getMethods().size());
+        }
+    }
+
+    public void testTimesOverloadedStringInt() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "times(String,int)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("AAA");
+
+        template.sendBodyAndHeader("direct:start", "A", "times", "3");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public void testTimesOverloadedBytesInt() throws Exception {
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start")
+                    .bean(MyBean.class, "times(byte[],int)")
+                    .to("mock:result");
+
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("ABC,ABC,ABC");
+
+        template.sendBodyAndHeader("direct:start", "ABC".getBytes(), "times", "3");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    public static final class MyBean {
+
+        public String hello(String name) {
+            return "Hello " + name;
+        }
+
+        public String hello(String name, @Header("country") String country) {
+            return "Hello " + name + " you are from " + country;
+        }
+
+        public String times(String name, @Header("times") int times) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < times; i++) {
+                sb.append(name);
+            }
+            return sb.toString();
+        }
+
+        public String times(byte[] data, @Header("times") int times) {
+            String s = new String(data);
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < times; i++) {
+                sb.append(s);
+                if (i < times - 1) {
+                    sb.append(",");
+                }
+            }
+            return sb.toString();
+        }
+
+        public String times(String name, int times, char separator) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < times; i++) {
+                sb.append(name);
+                if (i < times - 1) {
+                    sb.append(separator);
+                }
+            }
+            return sb.toString();
+        }
+
+    }
+}