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 }