001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.bean; 018 019 import java.lang.annotation.Annotation; 020 import java.lang.reflect.AccessibleObject; 021 import java.lang.reflect.AnnotatedElement; 022 import java.lang.reflect.InvocationTargetException; 023 import java.lang.reflect.Method; 024 import java.util.ArrayList; 025 import java.util.Arrays; 026 import java.util.List; 027 028 import org.apache.camel.Exchange; 029 import org.apache.camel.ExchangePattern; 030 import org.apache.camel.Expression; 031 import org.apache.camel.Pattern; 032 import org.apache.camel.util.ExchangeHelper; 033 import org.apache.camel.util.ObjectHelper; 034 import org.apache.commons.logging.Log; 035 import org.apache.commons.logging.LogFactory; 036 037 import static org.apache.camel.util.ObjectHelper.asString; 038 039 /** 040 * Information about a method to be used for invocation. 041 * 042 * @version $Revision: 47219 $ 043 */ 044 public class MethodInfo { 045 private static final transient Log LOG = LogFactory.getLog(MethodInfo.class); 046 047 private Class type; 048 private Method method; 049 private final List<ParameterInfo> parameters; 050 private final List<ParameterInfo> bodyParameters; 051 private final boolean hasCustomAnnotation; 052 private Expression parametersExpression; 053 private ExchangePattern pattern = ExchangePattern.InOut; 054 055 public MethodInfo(Class type, Method method, List<ParameterInfo> parameters, List<ParameterInfo> bodyParameters, boolean hasCustomAnnotation) { 056 this.type = type; 057 this.method = method; 058 this.parameters = parameters; 059 this.bodyParameters = bodyParameters; 060 this.hasCustomAnnotation = hasCustomAnnotation; 061 this.parametersExpression = createParametersExpression(); 062 Pattern oneway = findOneWayAnnotation(method); 063 if (oneway != null) { 064 pattern = oneway.value(); 065 } 066 } 067 068 public String toString() { 069 return method.toString(); 070 } 071 072 public MethodInvocation createMethodInvocation(final Object pojo, final Exchange messageExchange) { 073 final Object[] arguments = (Object[]) parametersExpression.evaluate(messageExchange); 074 return new MethodInvocation() { 075 public Method getMethod() { 076 return method; 077 } 078 079 public Object[] getArguments() { 080 return arguments; 081 } 082 083 public Object proceed() throws Throwable { 084 if (LOG.isTraceEnabled()) { 085 LOG.trace(">>>> invoking: " + method + " on bean: " + pojo + " with arguments: " + asString(arguments) + " for exchange: " + messageExchange); 086 } 087 return invoke(method, pojo, arguments, messageExchange); 088 } 089 090 public Object getThis() { 091 return pojo; 092 } 093 094 public AccessibleObject getStaticPart() { 095 return method; 096 } 097 }; 098 } 099 100 public Class getType() { 101 return type; 102 } 103 104 public Method getMethod() { 105 return method; 106 } 107 108 /** 109 * Returns the {@link org.apache.camel.ExchangePattern} that should be used when invoking this method. This value 110 * defaults to {@link org.apache.camel.ExchangePattern#InOut} unless some {@link org.apache.camel.Pattern} annotation is used 111 * to override the message exchange pattern. 112 * 113 * @return the exchange pattern to use for invoking this method. 114 */ 115 public ExchangePattern getPattern() { 116 return pattern; 117 } 118 119 public Expression getParametersExpression() { 120 return parametersExpression; 121 } 122 123 public List<ParameterInfo> getBodyParameters() { 124 return bodyParameters; 125 } 126 127 public Class getBodyParameterType() { 128 ParameterInfo parameterInfo = bodyParameters.get(0); 129 return parameterInfo.getType(); 130 } 131 132 public boolean bodyParameterMatches(Class bodyType) { 133 Class actualType = getBodyParameterType(); 134 return actualType != null && ObjectHelper.isAssignableFrom(bodyType, actualType); 135 } 136 137 public List<ParameterInfo> getParameters() { 138 return parameters; 139 } 140 141 public boolean hasBodyParameter() { 142 return !bodyParameters.isEmpty(); 143 } 144 145 public boolean isHasCustomAnnotation() { 146 return hasCustomAnnotation; 147 } 148 149 public boolean isReturnTypeVoid() { 150 return method.getReturnType().getName().equals("void"); 151 } 152 153 protected Object invoke(Method mth, Object pojo, Object[] arguments, Exchange exchange) throws IllegalAccessException, InvocationTargetException { 154 return mth.invoke(pojo, arguments); 155 } 156 157 protected Expression createParametersExpression() { 158 final int size = parameters.size(); 159 final Expression[] expressions = new Expression[size]; 160 for (int i = 0; i < size; i++) { 161 Expression parameterExpression = parameters.get(i).getExpression(); 162 expressions[i] = parameterExpression; 163 } 164 return new Expression<Exchange>() { 165 public Object evaluate(Exchange exchange) { 166 Object[] answer = new Object[size]; 167 Object body = exchange.getIn().getBody(); 168 boolean multiParameterArray = false; 169 if (exchange.getIn().getHeader(BeanProcessor.MULTI_PARAMETER_ARRAY) != null) { 170 multiParameterArray = exchange.getIn().getHeader(BeanProcessor.MULTI_PARAMETER_ARRAY, Boolean.class); 171 } 172 for (int i = 0; i < size; i++) { 173 Object value = null; 174 if (multiParameterArray) { 175 value = ((Object[])body)[i]; 176 } else { 177 value = expressions[i].evaluate(exchange); 178 } 179 // now lets try to coerce the value to the required type 180 Class expectedType = parameters.get(i).getType(); 181 value = ExchangeHelper.convertToType(exchange, expectedType, value); 182 answer[i] = value; 183 } 184 return answer; 185 } 186 187 @Override 188 public String toString() { 189 return "ParametersExpression: " + Arrays.asList(expressions); 190 } 191 }; 192 } 193 194 /** 195 * Finds the oneway annotation in priority order; look for method level annotations first, then the class level annotations, 196 * then super class annotations then interface annotations 197 * 198 * @param method the method on which to search 199 * @return the first matching annotation or none if it is not available 200 */ 201 protected Pattern findOneWayAnnotation(Method method) { 202 Pattern answer = getPatternAnnotation(method); 203 if (answer == null) { 204 Class<?> type = method.getDeclaringClass(); 205 206 // lets create the search order of types to scan 207 List<Class<?>> typesToSearch = new ArrayList<Class<?>>(); 208 addTypeAndSuperTypes(type, typesToSearch); 209 Class[] interfaces = type.getInterfaces(); 210 for (Class anInterface : interfaces) { 211 addTypeAndSuperTypes(anInterface, typesToSearch); 212 } 213 214 // now lets scan for a type which the current declared class overloads 215 answer = findOneWayAnnotationOnMethod(typesToSearch, method); 216 if (answer == null) { 217 answer = findOneWayAnnotation(typesToSearch); 218 } 219 } 220 return answer; 221 } 222 223 /** 224 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 225 * on an annotation which is also annotated 226 * 227 * @param annotatedElement the element to look for the annotation 228 * @return the first matching annotation or null if none could be found 229 */ 230 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement) { 231 return getPatternAnnotation(annotatedElement, 2); 232 } 233 234 /** 235 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 236 * on an annotation which is also annotated 237 * 238 * @param annotatedElement the element to look for the annotation 239 * @return the first matching annotation or null if none could be found 240 */ 241 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement, int depth) { 242 Pattern answer = annotatedElement.getAnnotation(Pattern.class); 243 int nextDepth = depth - 1; 244 245 if (nextDepth > 0) { 246 // lets look at all the annotations to see if any of those are annotated 247 Annotation[] annotations = annotatedElement.getAnnotations(); 248 for (Annotation annotation : annotations) { 249 Class<? extends Annotation> annotationType = annotation.annotationType(); 250 if (annotation instanceof Pattern || annotationType.equals(annotatedElement)) { 251 continue; 252 } else { 253 Pattern another = getPatternAnnotation(annotationType, nextDepth); 254 if (pattern != null) { 255 if (answer == null) { 256 answer = another; 257 } else { 258 LOG.warn("Duplicate pattern annotation: " + another + " found on annotation: " + annotation + " which will be ignored"); 259 } 260 } 261 } 262 } 263 } 264 return answer; 265 } 266 267 /** 268 * Adds the current class and all of its base classes (apart from {@link Object} to the given list 269 * @param type 270 * @param result 271 */ 272 protected void addTypeAndSuperTypes(Class<?> type, List<Class<?>> result) { 273 for (Class<?> t = type; t != null && t != Object.class; t = t.getSuperclass()) { 274 result.add(t); 275 } 276 } 277 278 /** 279 * Finds the first annotation on the base methods defined in the list of classes 280 */ 281 protected Pattern findOneWayAnnotationOnMethod(List<Class<?>> classes, Method method) { 282 for (Class<?> type : classes) { 283 try { 284 Method definedMethod = type.getMethod(method.getName(), method.getParameterTypes()); 285 Pattern answer = getPatternAnnotation(definedMethod); 286 if (answer != null) { 287 return answer; 288 } 289 } catch (NoSuchMethodException e) { 290 // ignore 291 } 292 } 293 return null; 294 } 295 296 297 /** 298 * Finds the first annotation on the given list of classes 299 */ 300 protected Pattern findOneWayAnnotation(List<Class<?>> classes) { 301 for (Class<?> type : classes) { 302 Pattern answer = getPatternAnnotation(type); 303 if (answer != null) { 304 return answer; 305 } 306 } 307 return null; 308 } 309 310 311 }