Author: davsclaus Date: Mon Jul 25 19:47:06 2011 New Revision: 1150865 URL: http://svn.apache.org/viewvc?rev=1150865&view=rev Log: CAMEL-3961: Bean component supports parameter values directly in the method name option.
Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanHelper.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/BeanOverloadedMethodParameterValueTest.java Added: camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanHelper.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanHelper.java?rev=1150865&view=auto ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanHelper.java (added) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/component/bean/BeanHelper.java Mon Jul 25 19:47:06 2011 @@ -0,0 +1,115 @@ +/** + * 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.language.simple.SimpleLanguage; +import org.apache.camel.spi.ClassResolver; +import org.apache.camel.util.ObjectHelper; + +/** + * + */ +public final class BeanHelper { + + private BeanHelper() { + // utility class + } + + /** + * Determines if the given value is valid according to the supported + * values by the bean component. + * + * @param value the value + * @return <tt>true</tt> if valid, <tt>false</tt> otherwise + */ + public static boolean isValidParameterValue(String value) { + if (ObjectHelper.isEmpty(value)) { + // empty value is valid + return true; + } + + // trim value + value = value.trim(); + + // single quoted is valid + if (value.startsWith("'") && value.endsWith("'")) { + return true; + } + + // double quoted is valid + if (value.startsWith("\"") && value.endsWith("\"")) { + return true; + } + + // true or false is valid (boolean) + if (value.equals("true") || value.equals("false")) { + return true; + } + + // simple language tokens is valid + if (SimpleLanguage.hasStartToken(value)) { + return true; + } + + // numeric is valid + boolean numeric = true; + for (char ch : value.toCharArray()) { + if (!Character.isDigit(ch)) { + numeric = false; + break; + } + } + return numeric; + } + + /** + * Determines if the given parameter type is assignable to the expected type. + * <p/> + * This implementation will check if the given parameter type matches the expected type as class using either + * <ul> + * <li>FQN class name - com.foo.MyOrder</li> + * <li>Simple class name - MyOrder</li> + * </ul> + * If the given parameter type is <b>not</b> a class, then <tt>false</tt> is returned + * + * @param resolver the class resolver + * @param parameterType the parameter type as a String, can be a FQN or a simple name of the class + * @param expectedType the expected type + * @return <tt>null</tt> if parameter type is <b>not</b> a class, <tt>true</tt> if parameter type is assignable, <tt>false</tt> otherwise + */ + public static Boolean isAssignableToExpectedType(ClassResolver resolver, String parameterType, Class<?> expectedType) { + // if its a class, then it should be assignable + Class<?> parameterClass = resolver.resolveClass(parameterType); + if (parameterClass == null && parameterType.equals(expectedType.getSimpleName())) { + // it was not the FQN class name, but the simple name instead, so we can infer the type + parameterClass = expectedType; + } + + // if there was a class, then it must be assignable to match + if (parameterClass != null) { + if (!parameterClass.isAssignableFrom(expectedType)) { + return false; + } else { + return true; + } + } + + // not a class so return null + return null; + } + +} 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=1150865&r1=1150864&r2=1150865&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 Jul 25 19:47:06 2011 @@ -717,10 +717,13 @@ public class BeanInfo { Iterator it = ObjectHelper.createIterator(types); for (int i = 0; i < method.getParameterTypes().length; i++) { if (it.hasNext()) { + Class<?> parameterType = method.getParameterTypes()[i]; + String qualifyType = (String) it.next(); if (ObjectHelper.isEmpty(qualifyType)) { continue; } + // trim the time qualifyType = qualifyType.trim(); if ("*".equals(qualifyType)) { @@ -728,27 +731,16 @@ public class BeanInfo { continue; } - if (qualifyType.startsWith("'") || qualifyType.startsWith("\"")) { - // if the type starts with a quote, then its a parameter value instead + if (BeanHelper.isValidParameterValue(qualifyType)) { + // its a parameter value, so continue to next parameter + // as we should only check for FQN/type parameters continue; } - // so it can either be a type or a parameter value - // - type: Boolean, String etc. - // - value: true, 5, 'Hello World' etc - - Class<?> qualifyClass = getCamelContext().getClassResolver().resolveClass(qualifyType); - // class resolver will return null if not a class - if (qualifyClass != null) { - Class<?> parameterClass = method.getParameterTypes()[i]; - if (!parameterClass.isAssignableFrom(qualifyClass)) { - return false; - } - } - - // maybe its a FQN for simple name - if (qualifyType.equals(method.getParameterTypes()[i].getSimpleName())) { - 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); + if (assignable != null && !assignable) { + return false; } } else { 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=1150865&r1=1150864&r2=1150865&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 Jul 25 19:47:06 2011 @@ -403,45 +403,43 @@ public class MethodInfo { private Object evaluateParameterValue(Exchange exchange, int index, Object parameterValue, Class<?> parameterType) { Object answer = null; - // at first evaluate it as a simple expression as it may contain references to headers, etc + // convert the parameter value to a String String exp = exchange.getContext().getTypeConverter().convertTo(String.class, parameterValue); if (exp != null) { // must trim first as there may be spaces between parameters exp = exp.trim(); + // check if its a valid parameter value + boolean valid = BeanHelper.isValidParameterValue(exp); - // TODO: make rule about how a parameter value should be defined - // and use this rule to pre determine if its class or not - // - boolean as true|false - // - numeric such as 5, 7, 123 - // - string literals which must be quoted, either single or double - - // so it can either be a type or a parameter value - // - type: Boolean, String etc. - // - value: true, 5, 'Hello World' etc - boolean isClass = false; - if (exp.startsWith("'") || exp.startsWith("\"")) { - // if the type starts with a quote, then its a parameter value - exp = StringHelper.removeLeadingAndEndingQuotes(exp); - } else { - // it could be a type, so lets so if we can resolve the class - Class<?> expClass = exchange.getContext().getClassResolver().resolveClass(exp); - if (expClass != null) { - isClass = true; - } else { - // lets try to match by simple name as well - if (exp.equals(parameterType.getSimpleName())) { - isClass = true; - } + if (!valid) { + // it may be a parameter type instead, and if so, then we should return null, + // as this method is only for evaluating parameter values + Boolean isClass = BeanHelper.isAssignableToExpectedType(exchange.getContext().getClassResolver(), exp, parameterType); + // the method will return a non null value if exp is a class + if (isClass != null) { + return null; } } - // if its a parameter value (not a class) then use the simple expression language to evaluate the expression - // and convert the result to the parameter type, so we can pass in the result to the method, when we invoke it - if (!isClass) { - parameterValue = exchange.getContext().resolveLanguage("simple").createExpression(exp).evaluate(exchange, Object.class); - if (parameterValue != null) { + // use simple language to evaluate the expression, as it may use the simple language to refer to message body, headers etc. + parameterValue = exchange.getContext().resolveLanguage("simple").createExpression(exp).evaluate(exchange, Object.class); + if (parameterValue != null) { + // the parameter value was not already valid, but since the simple language have evaluated the expression + // which may change the parameterValue, so we have to check it again to see if its now valid + exp = exchange.getContext().getTypeConverter().convertTo(String.class, parameterValue); + // String values from the simple language is always valid + if (!valid) { + // re validate if the parameter was not valid the first time (String values should be accepted) + valid = parameterValue instanceof String || BeanHelper.isValidParameterValue(exp); + } + + if (valid) { + // we need to unquote String parameters, as the enclosing quotes is there to denote a parameter value + if (parameterValue instanceof String) { + parameterValue = StringHelper.removeLeadingAndEndingQuotes((String) parameterValue); + } try { - // we got a value now try to convert it to the expected type + // its a valid parameter value, so convert it to the expected type of the parameter answer = exchange.getContext().getTypeConverter().mandatoryConvertTo(parameterType, parameterValue); if (LOG.isTraceEnabled()) { LOG.trace("Parameter #{} evaluated as: {} type: ", new Object[]{index, answer, ObjectHelper.type(answer)}); Modified: camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodParameterValueTest.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodParameterValueTest.java?rev=1150865&r1=1150864&r2=1150865&view=diff ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodParameterValueTest.java (original) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/component/bean/BeanOverloadedMethodParameterValueTest.java Mon Jul 25 19:47:06 2011 @@ -149,6 +149,26 @@ public class BeanOverloadedMethodParamet @Override public void configure() throws Exception { from("direct:start") + .bean(MyBean.class, "times(byte[], header.times)") + .to("mock:result"); + + } + }); + context.start(); + + getMockEndpoint("mock:result").expectedBodiesReceived("ABC,ABC,ABC"); + + template.sendBodyAndHeader("direct:start", "ABC".getBytes(), "times", "3"); + + assertMockEndpointsSatisfied(); + } + + + public void testTimesOverloadedBytesIntLanguageTokens() throws Exception { + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") .bean(MyBean.class, "times(byte[],${header.times})") .to("mock:result");