This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
commit bd857c0a18e18b4967acdb0c723b1d4b16def0c9 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Apr 4 12:16:05 2023 +0200 CAMEL-19244: camel-bean - Add support for declaring type in parameter values using 'type.class value' style --- .../bean/AmbiguousMethodCallException.java | 17 ++++- .../org/apache/camel/component/bean/BeanInfo.java | 30 ++++++--- .../apache/camel/model/ProcessorDefinition.java | 10 ++- .../component/bean/BeanOverloadedMethodTest.java | 2 +- .../bean/BeanOverloadsWithAssignableParamTest.java | 2 +- .../bean/issues/BeanParameterTypeAndValueTest.java | 74 ++++++++++++++++++++++ .../modules/ROOT/pages/bean-binding.adoc | 20 +++++- 7 files changed, 138 insertions(+), 17 deletions(-) diff --git a/components/camel-bean/src/main/java/org/apache/camel/component/bean/AmbiguousMethodCallException.java b/components/camel-bean/src/main/java/org/apache/camel/component/bean/AmbiguousMethodCallException.java index 2ebe949c515..1cda5179744 100644 --- a/components/camel-bean/src/main/java/org/apache/camel/component/bean/AmbiguousMethodCallException.java +++ b/components/camel-bean/src/main/java/org/apache/camel/component/bean/AmbiguousMethodCallException.java @@ -17,6 +17,7 @@ package org.apache.camel.component.bean; import java.util.Collection; +import java.util.StringJoiner; import org.apache.camel.Exchange; import org.apache.camel.RuntimeExchangeException; @@ -30,7 +31,7 @@ public class AmbiguousMethodCallException extends RuntimeExchangeException { private final Collection<MethodInfo> methods; public AmbiguousMethodCallException(Exchange exchange, Collection<MethodInfo> methods) { - super("Ambiguous method invocations possible: " + methods, exchange); + super(createMessage(methods), exchange); this.methods = methods; } @@ -40,4 +41,18 @@ public class AmbiguousMethodCallException extends RuntimeExchangeException { public Collection<MethodInfo> getMethods() { return methods; } + + private static String createMessage(Collection<MethodInfo> methods) { + Class<?> clazz = null; + StringJoiner sj = new StringJoiner("\n\t"); + for (MethodInfo mi : methods) { + if (clazz == null) { + clazz = mi.getMethod().getDeclaringClass(); + sj.add("\tClass: " + clazz.getName()); + } + sj.add("\t" + mi.getMethod().toGenericString()); + } + return "Ambiguous method invocations possible:\n" + sj + "\n\n"; + } + } diff --git a/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java b/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java index b918711dc1a..c0f59bf911f 100644 --- a/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java +++ b/components/camel-bean/src/main/java/org/apache/camel/component/bean/BeanInfo.java @@ -244,7 +244,7 @@ public class BeanInfo { } } - if (methodInfo == null || name != null && !name.equals(methodInfo.getMethod().getName())) { + if (methodInfo == null || !name.equals(methodInfo.getMethod().getName())) { throw new AmbiguousMethodCallException(exchange, methods); } } else { @@ -1091,7 +1091,7 @@ public class BeanInfo { String types = StringHelper.betweenOuterPair(methodName, '(', ')'); if (org.apache.camel.util.ObjectHelper.isNotEmpty(types)) { // we must qualify based on types to match method - String[] parameters = StringQuoteHelper.splitSafeQuote(types, ','); + String[] parameters = StringQuoteHelper.splitSafeQuote(types, ',', true, true); Class<?>[] parameterTypes = null; Iterator<?> it = ObjectHelper.createIterator(parameters); for (int i = 0; i < method.getParameterCount(); i++) { @@ -1107,18 +1107,23 @@ public class BeanInfo { } // trim the type qualifyType = qualifyType.trim(); + String value = qualifyType; + int pos1 = qualifyType.indexOf(' '); + int pos2 = qualifyType.indexOf(".class"); + if (pos1 != -1 && pos2 != -1 && pos1 > pos2) { + // a parameter can include type in the syntax to help with choosing correct method + // therefore we need to check if type is provided in syntax (name.class value, name2.class value2, ...) + value = qualifyType.substring(pos1); + value = value.trim(); + qualifyType = qualifyType.substring(0, pos1); + qualifyType = qualifyType.trim(); + } if ("*".equals(qualifyType)) { // * is a wildcard so we accept and match that parameter type continue; } - if (BeanHelper.isValidParameterValue(getCamelContext().getClassResolver(), qualifyType)) { - // its a parameter value, so continue to next parameter - // as we should only check for FQN/type parameters - continue; - } - // if qualify type indeed is a class, then it must be assignable with the parameter type Boolean assignable = BeanHelper.isAssignableToExpectedType(getCamelContext().getClassResolver(), qualifyType, parameterType); @@ -1127,6 +1132,13 @@ public class BeanInfo { return false; } + if (!qualifyType.endsWith(".class") + && !BeanHelper.isValidParameterValue(getCamelContext().getClassResolver(), value)) { + // its a parameter value, so continue to next parameter + // as we should only check for FQN/type parameters + return false; + } + } else { // there method has more parameters than was specified in the method name qualifiers return false; @@ -1157,7 +1169,7 @@ public class BeanInfo { } /** - * Do we have a method with the given name. + * Do we have a method with the given name? * <p/> * Shorthand method names for getters is supported, so you can pass in eg 'name' and Camel will can find the real * 'getName' method instead. diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java index 5e1519d9b97..a58bb3afb76 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/ProcessorDefinition.java @@ -2245,14 +2245,20 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type> * <a href="http://camel.apache.org/message-translator.html">Message Translator EIP:</a> Adds a bean which is * invoked which could be a final destination, or could be a transformation in a pipeline * - * @param bean the bean to invoke, or a reference to a bean if the type is a String + * @param bean the bean to invoke (if String then a reference to a bean, prefix with type: to specify FQN java + * class) * @param method the method name to invoke on the bean (can be used to avoid ambiguity) * @return the builder */ public Type bean(Object bean, String method) { BeanDefinition answer = new BeanDefinition(); if (bean instanceof String) { - answer.setRef((String) bean); + String str = (String) bean; + if (str.startsWith("type:")) { + answer.setBeanType(str.substring(5)); + } else { + answer.setRef(str); + } } else { answer.setBean(bean); } diff --git a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java index 1655eb68e9d..9753ef5751e 100644 --- a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodTest.java @@ -36,7 +36,7 @@ public class BeanOverloadedMethodTest extends ContextTestSupport { } @Test - public void testHelloOverloadedHeString() throws Exception { + public void testHelloOverloadedString() throws Exception { context.addRoutes(new RouteBuilder() { @Override public void configure() throws Exception { diff --git a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadsWithAssignableParamTest.java b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadsWithAssignableParamTest.java index 8b6e11023b8..b572995abb8 100644 --- a/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadsWithAssignableParamTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadsWithAssignableParamTest.java @@ -36,7 +36,7 @@ public class BeanOverloadsWithAssignableParamTest extends ContextTestSupport { return new RouteBuilder() { @Override public void configure() throws Exception { - from("direct:stringParam").bean(new MyOtherFooBean(), "toString(String)").to("mock:stringParamResult"); + from("direct:stringParam").bean(new MyOtherFooBean(), "toString(String.class)").to("mock:stringParamResult"); } }; } diff --git a/core/camel-core/src/test/java/org/apache/camel/component/bean/issues/BeanParameterTypeAndValueTest.java b/core/camel-core/src/test/java/org/apache/camel/component/bean/issues/BeanParameterTypeAndValueTest.java new file mode 100644 index 00000000000..38673ad1048 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/component/bean/issues/BeanParameterTypeAndValueTest.java @@ -0,0 +1,74 @@ +/* + * 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.issues; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +public class BeanParameterTypeAndValueTest extends ContextTestSupport { + + @Test + public void testBean() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("123"); + getMockEndpoint("mock:result").message(0).body().isInstanceOf(Integer.class); + + template.sendBody("direct:bean", "Hello World"); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testBean2() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("44"); + getMockEndpoint("mock:result").message(0).body().isInstanceOf(Integer.class); + + template.sendBody("direct:bean2", "Hi World"); + + assertMockEndpointsSatisfied(); + } + + @Test + public void testSimple() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("7"); + getMockEndpoint("mock:result").message(0).body().isInstanceOf(Integer.class); + + template.sendBody("direct:simple", "Bye World"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:bean") + .bean(Math.class, "abs(int.class -123)") + .to("mock:result"); + + from("direct:bean2") + .bean("type:java.lang.Math", "abs(int.class -44)") + .to("mock:result"); + + from("direct:simple") + .setBody().simple("${bean:type:java.lang.Math?method=abs(int.class -7)}") + .to("mock:result"); + } + }; + } +} diff --git a/docs/user-manual/modules/ROOT/pages/bean-binding.adoc b/docs/user-manual/modules/ROOT/pages/bean-binding.adoc index 6236a3334e4..f5dce7d1489 100644 --- a/docs/user-manual/modules/ROOT/pages/bean-binding.adoc +++ b/docs/user-manual/modules/ROOT/pages/bean-binding.adoc @@ -369,11 +369,25 @@ the FQN, and *not* the simple name "MyOrder", then follow this example: .bean(OrderService.class, "doSomething(com.foo.MyOrder.class)") ---- -Camel currently only supports either specifying parameter binding or +=== Declaring parameter type and value + +*Available as of Camel 4.0* + +Camel 3.x only supports either specifying parameter binding or type per parameter in the method name option. You *cannot* specify both -at the same time, such as +at the same time, such as: + +[source,text] +---- +doSomething(com.foo.MyOrder.class ${body}, boolean ${header.high}, int 123) +---- + +However, we have implemented support for this in Camel 4, +where you can declare both using _name.class value_ syntax as shown: [source,text] ---- -doSomething(com.foo.MyOrder.class ${body}, boolean ${header.high}) +doSomething(com.foo.MyOrder.class ${body}, boolean.class ${header.high}, int.class 123) ---- + +Notice that you *MUST* use `name.class` when declaring the type, also for String, int, boolean etc.
