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: 704061 $
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 }