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.impl.ExpressionAdapter; 033 import org.apache.camel.processor.RecipientList; 034 import org.apache.camel.util.ObjectHelper; 035 import org.apache.commons.logging.Log; 036 import org.apache.commons.logging.LogFactory; 037 038 import static org.apache.camel.util.ObjectHelper.asString; 039 040 /** 041 * Information about a method to be used for invocation. 042 * 043 * @version $Revision: 749201 $ 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(); 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 ExpressionAdapter() { 174 @SuppressWarnings("unchecked") 175 public Object evaluate(Exchange exchange) { 176 Object[] answer = new Object[size]; 177 Object body = exchange.getIn().getBody(); 178 boolean multiParameterArray = false; 179 if (exchange.getIn().getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY) != null) { 180 multiParameterArray = exchange.getIn().getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY, Boolean.class); 181 } 182 for (int i = 0; i < size; i++) { 183 Object value = null; 184 if (multiParameterArray) { 185 value = ((Object[])body)[i]; 186 } else { 187 value = expressions[i].evaluate(exchange, parameters.get(i).getType()); 188 } 189 // now lets try to coerce the value to the required type 190 answer[i] = value; 191 } 192 return answer; 193 } 194 195 @Override 196 public String toString() { 197 return "ParametersExpression: " + Arrays.asList(expressions); 198 } 199 }; 200 } 201 202 /** 203 * Finds the oneway annotation in priority order; look for method level annotations first, then the class level annotations, 204 * then super class annotations then interface annotations 205 * 206 * @param method the method on which to search 207 * @return the first matching annotation or none if it is not available 208 */ 209 protected Pattern findOneWayAnnotation(Method method) { 210 Pattern answer = getPatternAnnotation(method); 211 if (answer == null) { 212 Class<?> type = method.getDeclaringClass(); 213 214 // lets create the search order of types to scan 215 List<Class<?>> typesToSearch = new ArrayList<Class<?>>(); 216 addTypeAndSuperTypes(type, typesToSearch); 217 Class[] interfaces = type.getInterfaces(); 218 for (Class anInterface : interfaces) { 219 addTypeAndSuperTypes(anInterface, typesToSearch); 220 } 221 222 // now lets scan for a type which the current declared class overloads 223 answer = findOneWayAnnotationOnMethod(typesToSearch, method); 224 if (answer == null) { 225 answer = findOneWayAnnotation(typesToSearch); 226 } 227 } 228 return answer; 229 } 230 231 /** 232 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 233 * on an annotation which is also annotated 234 * 235 * @param annotatedElement the element to look for the annotation 236 * @return the first matching annotation or null if none could be found 237 */ 238 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement) { 239 return getPatternAnnotation(annotatedElement, 2); 240 } 241 242 /** 243 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 244 * on an annotation which is also annotated 245 * 246 * @param annotatedElement the element to look for the annotation 247 * @param depth the current depth 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 */ 279 protected void addTypeAndSuperTypes(Class<?> type, List<Class<?>> result) { 280 for (Class<?> t = type; t != null && t != Object.class; t = t.getSuperclass()) { 281 result.add(t); 282 } 283 } 284 285 /** 286 * Finds the first annotation on the base methods defined in the list of classes 287 */ 288 protected Pattern findOneWayAnnotationOnMethod(List<Class<?>> classes, Method method) { 289 for (Class<?> type : classes) { 290 try { 291 Method definedMethod = type.getMethod(method.getName(), method.getParameterTypes()); 292 Pattern answer = getPatternAnnotation(definedMethod); 293 if (answer != null) { 294 return answer; 295 } 296 } catch (NoSuchMethodException e) { 297 // ignore 298 } 299 } 300 return null; 301 } 302 303 304 /** 305 * Finds the first annotation on the given list of classes 306 */ 307 protected Pattern findOneWayAnnotation(List<Class<?>> classes) { 308 for (Class<?> type : classes) { 309 Pattern answer = getPatternAnnotation(type); 310 if (answer != null) { 311 return answer; 312 } 313 } 314 return null; 315 } 316 317 318 }