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.processor.RecipientList; 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: 782977 $ 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 final boolean hasHandlerAnnotation; 053 private Expression parametersExpression; 054 private ExchangePattern pattern = ExchangePattern.InOut; 055 private RecipientList recipientList; 056 057 public MethodInfo(Class type, Method method, List<ParameterInfo> parameters, List<ParameterInfo> bodyParameters, 058 boolean hasCustomAnnotation, boolean hasHandlerAnnotation) { 059 this.type = type; 060 this.method = method; 061 this.parameters = parameters; 062 this.bodyParameters = bodyParameters; 063 this.hasCustomAnnotation = hasCustomAnnotation; 064 this.hasHandlerAnnotation = hasHandlerAnnotation; 065 this.parametersExpression = createParametersExpression(); 066 Pattern oneway = findOneWayAnnotation(method); 067 if (oneway != null) { 068 pattern = oneway.value(); 069 } 070 if (method.getAnnotation(org.apache.camel.RecipientList.class) != null) { 071 recipientList = new RecipientList(); 072 } 073 } 074 075 public String toString() { 076 return method.toString(); 077 } 078 079 public MethodInvocation createMethodInvocation(final Object pojo, final Exchange exchange) { 080 final Object[] arguments = parametersExpression.evaluate(exchange, Object[].class); 081 return new MethodInvocation() { 082 public Method getMethod() { 083 return method; 084 } 085 086 public Object[] getArguments() { 087 return arguments; 088 } 089 090 public Object proceed() throws Exception { 091 if (LOG.isTraceEnabled()) { 092 LOG.trace(">>>> invoking: " + method + " on bean: " + pojo + " with arguments: " + asString(arguments) + " for exchange: " + exchange); 093 } 094 Object result = invoke(method, pojo, arguments, exchange); 095 if (recipientList != null) { 096 recipientList.sendToRecipientList(exchange, result); 097 } 098 return result; 099 } 100 101 public Object getThis() { 102 return pojo; 103 } 104 105 public AccessibleObject getStaticPart() { 106 return method; 107 } 108 }; 109 } 110 111 public Class getType() { 112 return type; 113 } 114 115 public Method getMethod() { 116 return method; 117 } 118 119 /** 120 * Returns the {@link org.apache.camel.ExchangePattern} that should be used when invoking this method. This value 121 * defaults to {@link org.apache.camel.ExchangePattern#InOut} unless some {@link org.apache.camel.Pattern} annotation is used 122 * to override the message exchange pattern. 123 * 124 * @return the exchange pattern to use for invoking this method. 125 */ 126 public ExchangePattern getPattern() { 127 return pattern; 128 } 129 130 public Expression getParametersExpression() { 131 return parametersExpression; 132 } 133 134 public List<ParameterInfo> getBodyParameters() { 135 return bodyParameters; 136 } 137 138 public Class getBodyParameterType() { 139 if (bodyParameters.isEmpty()) { 140 return null; 141 } 142 ParameterInfo parameterInfo = bodyParameters.get(0); 143 return parameterInfo.getType(); 144 } 145 146 public boolean bodyParameterMatches(Class bodyType) { 147 Class actualType = getBodyParameterType(); 148 return actualType != null && ObjectHelper.isAssignableFrom(bodyType, actualType); 149 } 150 151 public List<ParameterInfo> getParameters() { 152 return parameters; 153 } 154 155 public boolean hasBodyParameter() { 156 return !bodyParameters.isEmpty(); 157 } 158 159 public boolean hasCustomAnnotation() { 160 return hasCustomAnnotation; 161 } 162 163 public boolean hasHandlerAnnotation() { 164 return hasHandlerAnnotation; 165 } 166 167 public boolean isReturnTypeVoid() { 168 return method.getReturnType().getName().equals("void"); 169 } 170 171 protected Object invoke(Method mth, Object pojo, Object[] arguments, Exchange exchange) throws IllegalAccessException, InvocationTargetException { 172 return mth.invoke(pojo, arguments); 173 } 174 175 protected Expression createParametersExpression() { 176 final int size = parameters.size(); 177 if (LOG.isTraceEnabled()) { 178 LOG.trace("Creating parameters expression for " + size + " parameters"); 179 } 180 181 final Expression[] expressions = new Expression[size]; 182 for (int i = 0; i < size; i++) { 183 Expression parameterExpression = parameters.get(i).getExpression(); 184 expressions[i] = parameterExpression; 185 if (LOG.isTraceEnabled()) { 186 LOG.trace("Parameter #" + i + " has expression: " + parameterExpression); 187 } 188 } 189 return new Expression() { 190 @SuppressWarnings("unchecked") 191 public <T> T evaluate(Exchange exchange, Class<T> type) { 192 Object[] answer = new Object[size]; 193 Object body = exchange.getIn().getBody(); 194 boolean multiParameterArray = false; 195 if (exchange.getIn().getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY) != null) { 196 multiParameterArray = exchange.getIn().getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY, Boolean.class); 197 } 198 for (int i = 0; i < size; i++) { 199 Object value = null; 200 if (multiParameterArray) { 201 value = ((Object[])body)[i]; 202 } else { 203 Expression expression = expressions[i]; 204 if (expression != null) { 205 value = expression.evaluate(exchange, parameters.get(i).getType()); 206 if (LOG.isTraceEnabled()) { 207 LOG.trace("Parameter #" + i + " evaluated as: " + value + " type: " + ObjectHelper.type(value)); 208 } 209 } 210 } 211 // now lets try to coerce the value to the required type 212 answer[i] = value; 213 } 214 return (T) answer; 215 } 216 217 @Override 218 public String toString() { 219 return "ParametersExpression: " + Arrays.asList(expressions); 220 } 221 222 }; 223 } 224 225 /** 226 * Finds the oneway annotation in priority order; look for method level annotations first, then the class level annotations, 227 * then super class annotations then interface annotations 228 * 229 * @param method the method on which to search 230 * @return the first matching annotation or none if it is not available 231 */ 232 protected Pattern findOneWayAnnotation(Method method) { 233 Pattern answer = getPatternAnnotation(method); 234 if (answer == null) { 235 Class<?> type = method.getDeclaringClass(); 236 237 // lets create the search order of types to scan 238 List<Class<?>> typesToSearch = new ArrayList<Class<?>>(); 239 addTypeAndSuperTypes(type, typesToSearch); 240 Class[] interfaces = type.getInterfaces(); 241 for (Class anInterface : interfaces) { 242 addTypeAndSuperTypes(anInterface, typesToSearch); 243 } 244 245 // now lets scan for a type which the current declared class overloads 246 answer = findOneWayAnnotationOnMethod(typesToSearch, method); 247 if (answer == null) { 248 answer = findOneWayAnnotation(typesToSearch); 249 } 250 } 251 return answer; 252 } 253 254 /** 255 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 256 * on an annotation which is also annotated 257 * 258 * @param annotatedElement the element to look for the annotation 259 * @return the first matching annotation or null if none could be found 260 */ 261 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement) { 262 return getPatternAnnotation(annotatedElement, 2); 263 } 264 265 /** 266 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 267 * on an annotation which is also annotated 268 * 269 * @param annotatedElement the element to look for the annotation 270 * @param depth the current depth 271 * @return the first matching annotation or null if none could be found 272 */ 273 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement, int depth) { 274 Pattern answer = annotatedElement.getAnnotation(Pattern.class); 275 int nextDepth = depth - 1; 276 277 if (nextDepth > 0) { 278 // lets look at all the annotations to see if any of those are annotated 279 Annotation[] annotations = annotatedElement.getAnnotations(); 280 for (Annotation annotation : annotations) { 281 Class<? extends Annotation> annotationType = annotation.annotationType(); 282 if (annotation instanceof Pattern || annotationType.equals(annotatedElement)) { 283 continue; 284 } else { 285 Pattern another = getPatternAnnotation(annotationType, nextDepth); 286 if (pattern != null) { 287 if (answer == null) { 288 answer = another; 289 } else { 290 LOG.warn("Duplicate pattern annotation: " + another + " found on annotation: " + annotation + " which will be ignored"); 291 } 292 } 293 } 294 } 295 } 296 return answer; 297 } 298 299 /** 300 * Adds the current class and all of its base classes (apart from {@link Object} to the given list 301 */ 302 protected void addTypeAndSuperTypes(Class<?> type, List<Class<?>> result) { 303 for (Class<?> t = type; t != null && t != Object.class; t = t.getSuperclass()) { 304 result.add(t); 305 } 306 } 307 308 /** 309 * Finds the first annotation on the base methods defined in the list of classes 310 */ 311 protected Pattern findOneWayAnnotationOnMethod(List<Class<?>> classes, Method method) { 312 for (Class<?> type : classes) { 313 try { 314 Method definedMethod = type.getMethod(method.getName(), method.getParameterTypes()); 315 Pattern answer = getPatternAnnotation(definedMethod); 316 if (answer != null) { 317 return answer; 318 } 319 } catch (NoSuchMethodException e) { 320 // ignore 321 } 322 } 323 return null; 324 } 325 326 327 /** 328 * Finds the first annotation on the given list of classes 329 */ 330 protected Pattern findOneWayAnnotation(List<Class<?>> classes) { 331 for (Class<?> type : classes) { 332 Pattern answer = getPatternAnnotation(type); 333 if (answer != null) { 334 return answer; 335 } 336 } 337 return null; 338 } 339 340 protected boolean hasExceptionParameter() { 341 for (ParameterInfo parameter : parameters) { 342 if (Exception.class.isAssignableFrom(parameter.getType())) { 343 return true; 344 } 345 } 346 return false; 347 } 348 349 }