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