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