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