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();
+ }
+
+ }
+}