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 }