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.lang.reflect.Modifier; 025 import java.util.ArrayList; 026 import java.util.Arrays; 027 import java.util.HashMap; 028 import java.util.Iterator; 029 import java.util.List; 030 import java.util.Map; 031 import java.util.concurrent.ExecutorService; 032 033 import org.apache.camel.AsyncCallback; 034 import org.apache.camel.CamelContext; 035 import org.apache.camel.Exchange; 036 import org.apache.camel.ExchangePattern; 037 import org.apache.camel.Expression; 038 import org.apache.camel.ExpressionEvaluationException; 039 import org.apache.camel.NoTypeConversionAvailableException; 040 import org.apache.camel.Pattern; 041 import org.apache.camel.Processor; 042 import org.apache.camel.RuntimeExchangeException; 043 import org.apache.camel.processor.DynamicRouter; 044 import org.apache.camel.processor.RecipientList; 045 import org.apache.camel.processor.RoutingSlip; 046 import org.apache.camel.processor.aggregate.AggregationStrategy; 047 import org.apache.camel.support.ExpressionAdapter; 048 import org.apache.camel.util.CamelContextHelper; 049 import org.apache.camel.util.ObjectHelper; 050 import org.apache.camel.util.ServiceHelper; 051 import org.apache.camel.util.StringHelper; 052 import org.apache.camel.util.StringQuoteHelper; 053 import org.slf4j.Logger; 054 import org.slf4j.LoggerFactory; 055 056 import static org.apache.camel.util.ObjectHelper.asString; 057 /** 058 * Information about a method to be used for invocation. 059 * 060 * @version 061 */ 062 public class MethodInfo { 063 private static final transient Logger LOG = LoggerFactory.getLogger(MethodInfo.class); 064 065 private CamelContext camelContext; 066 private Class<?> type; 067 private Method method; 068 private final List<ParameterInfo> parameters; 069 private final List<ParameterInfo> bodyParameters; 070 private final boolean hasCustomAnnotation; 071 private final boolean hasHandlerAnnotation; 072 private Expression parametersExpression; 073 private ExchangePattern pattern = ExchangePattern.InOut; 074 private RecipientList recipientList; 075 private RoutingSlip routingSlip; 076 private DynamicRouter dynamicRouter; 077 078 /** 079 * Adapter to invoke the method which has been annotated with the @DynamicRouter 080 */ 081 private final class DynamicRouterExpression extends ExpressionAdapter { 082 private final Object pojo; 083 084 private DynamicRouterExpression(Object pojo) { 085 this.pojo = pojo; 086 } 087 088 @Override 089 public Object evaluate(Exchange exchange) { 090 // evaluate arguments on each invocation as the parameters can have changed/updated since last invocation 091 final Object[] arguments = parametersExpression.evaluate(exchange, Object[].class); 092 try { 093 return invoke(method, pojo, arguments, exchange); 094 } catch (Exception e) { 095 throw ObjectHelper.wrapRuntimeCamelException(e); 096 } 097 } 098 099 @Override 100 public String toString() { 101 return "DynamicRouter[invoking: " + method + " on bean: " + pojo + "]"; 102 } 103 } 104 105 @SuppressWarnings("deprecation") 106 public MethodInfo(CamelContext camelContext, Class<?> type, Method method, List<ParameterInfo> parameters, List<ParameterInfo> bodyParameters, 107 boolean hasCustomAnnotation, boolean hasHandlerAnnotation) { 108 this.camelContext = camelContext; 109 this.type = type; 110 this.method = method; 111 this.parameters = parameters; 112 this.bodyParameters = bodyParameters; 113 this.hasCustomAnnotation = hasCustomAnnotation; 114 this.hasHandlerAnnotation = hasHandlerAnnotation; 115 this.parametersExpression = createParametersExpression(); 116 117 Map<Class<?>, Annotation> collectedMethodAnnotation = collectMethodAnnotations(type, method); 118 119 Pattern oneway = findOneWayAnnotation(method); 120 if (oneway != null) { 121 pattern = oneway.value(); 122 } 123 124 org.apache.camel.RoutingSlip routingSlipAnnotation = 125 (org.apache.camel.RoutingSlip)collectedMethodAnnotation.get(org.apache.camel.RoutingSlip.class); 126 if (routingSlipAnnotation != null && matchContext(routingSlipAnnotation.context())) { 127 routingSlip = new RoutingSlip(camelContext); 128 routingSlip.setDelimiter(routingSlipAnnotation.delimiter()); 129 routingSlip.setIgnoreInvalidEndpoints(routingSlipAnnotation.ignoreInvalidEndpoints()); 130 // add created routingSlip as a service so we have its lifecycle managed 131 try { 132 camelContext.addService(routingSlip); 133 } catch (Exception e) { 134 throw ObjectHelper.wrapRuntimeCamelException(e); 135 } 136 } 137 138 org.apache.camel.DynamicRouter dynamicRouterAnnotation = 139 (org.apache.camel.DynamicRouter)collectedMethodAnnotation.get(org.apache.camel.DynamicRouter.class); 140 if (dynamicRouterAnnotation != null 141 && matchContext(dynamicRouterAnnotation.context())) { 142 dynamicRouter = new DynamicRouter(camelContext); 143 dynamicRouter.setDelimiter(dynamicRouterAnnotation.delimiter()); 144 dynamicRouter.setIgnoreInvalidEndpoints(dynamicRouterAnnotation.ignoreInvalidEndpoints()); 145 // add created dynamicRouter as a service so we have its lifecycle managed 146 try { 147 camelContext.addService(dynamicRouter); 148 } catch (Exception e) { 149 throw ObjectHelper.wrapRuntimeCamelException(e); 150 } 151 } 152 153 org.apache.camel.RecipientList recipientListAnnotation = 154 (org.apache.camel.RecipientList)collectedMethodAnnotation.get(org.apache.camel.RecipientList.class); 155 if (recipientListAnnotation != null 156 && matchContext(recipientListAnnotation.context())) { 157 recipientList = new RecipientList(camelContext, recipientListAnnotation.delimiter()); 158 recipientList.setStopOnException(recipientListAnnotation.stopOnException()); 159 recipientList.setIgnoreInvalidEndpoints(recipientListAnnotation.ignoreInvalidEndpoints()); 160 recipientList.setParallelProcessing(recipientListAnnotation.parallelProcessing()); 161 recipientList.setStreaming(recipientListAnnotation.streaming()); 162 recipientList.setTimeout(recipientListAnnotation.timeout()); 163 recipientList.setShareUnitOfWork(recipientListAnnotation.shareUnitOfWork()); 164 165 if (ObjectHelper.isNotEmpty(recipientListAnnotation.executorServiceRef())) { 166 ExecutorService executor = camelContext.getExecutorServiceManager().newDefaultThreadPool(this, recipientListAnnotation.executorServiceRef()); 167 recipientList.setExecutorService(executor); 168 } 169 170 if (recipientListAnnotation.parallelProcessing() && recipientList.getExecutorService() == null) { 171 // we are running in parallel so we need a thread pool 172 ExecutorService executor = camelContext.getExecutorServiceManager().newDefaultThreadPool(this, "@RecipientList"); 173 recipientList.setExecutorService(executor); 174 } 175 176 if (ObjectHelper.isNotEmpty(recipientListAnnotation.strategyRef())) { 177 AggregationStrategy strategy = CamelContextHelper.mandatoryLookup(camelContext, recipientListAnnotation.strategyRef(), AggregationStrategy.class); 178 recipientList.setAggregationStrategy(strategy); 179 } 180 181 if (ObjectHelper.isNotEmpty(recipientListAnnotation.onPrepareRef())) { 182 Processor onPrepare = CamelContextHelper.mandatoryLookup(camelContext, recipientListAnnotation.onPrepareRef(), Processor.class); 183 recipientList.setOnPrepare(onPrepare); 184 } 185 186 // add created recipientList as a service so we have its lifecycle managed 187 try { 188 camelContext.addService(recipientList); 189 } catch (Exception e) { 190 throw ObjectHelper.wrapRuntimeCamelException(e); 191 } 192 } 193 } 194 195 private Map<Class<?>, Annotation> collectMethodAnnotations(Class<?> c, Method method) { 196 Map<Class<?>, Annotation> annotations = new HashMap<Class<?>, Annotation>(); 197 collectMethodAnnotations(c, method, annotations); 198 return annotations; 199 } 200 201 private void collectMethodAnnotations(Class<?> c, Method method, Map<Class<?>, Annotation> annotations) { 202 for (Class<?> i : c.getInterfaces()) { 203 collectMethodAnnotations(i, method, annotations); 204 } 205 if (!c.isInterface() && c.getSuperclass() != null) { 206 collectMethodAnnotations(c.getSuperclass(), method, annotations); 207 } 208 // make sure the sub class can override the definition 209 try { 210 Annotation[] ma = c.getDeclaredMethod(method.getName(), method.getParameterTypes()).getAnnotations(); 211 for (Annotation a : ma) { 212 annotations.put(a.annotationType(), a); 213 } 214 } catch (SecurityException e) { 215 // do nothing here 216 } catch (NoSuchMethodException e) { 217 // do nothing here 218 } 219 } 220 221 /** 222 * Does the given context match this camel context 223 */ 224 private boolean matchContext(String context) { 225 if (ObjectHelper.isNotEmpty(context)) { 226 if (!camelContext.getName().equals(context)) { 227 return false; 228 } 229 } 230 return true; 231 } 232 233 public String toString() { 234 return method.toString(); 235 } 236 237 public MethodInvocation createMethodInvocation(final Object pojo, final Exchange exchange) { 238 final Object[] arguments = parametersExpression.evaluate(exchange, Object[].class); 239 return new MethodInvocation() { 240 public Method getMethod() { 241 return method; 242 } 243 244 public Object[] getArguments() { 245 return arguments; 246 } 247 248 public boolean proceed(AsyncCallback callback) { 249 try { 250 return doProceed(callback); 251 } catch (InvocationTargetException e) { 252 exchange.setException(e.getTargetException()); 253 callback.done(true); 254 return true; 255 } catch (Throwable e) { 256 exchange.setException(e); 257 callback.done(true); 258 return true; 259 } 260 } 261 262 private boolean doProceed(AsyncCallback callback) throws Exception { 263 // dynamic router should be invoked beforehand 264 if (dynamicRouter != null) { 265 if (!dynamicRouter.isStarted()) { 266 ServiceHelper.startService(dynamicRouter); 267 } 268 // use a expression which invokes the method to be used by dynamic router 269 Expression expression = new DynamicRouterExpression(pojo); 270 return dynamicRouter.doRoutingSlip(exchange, expression, callback); 271 } 272 273 // invoke pojo 274 if (LOG.isTraceEnabled()) { 275 LOG.trace(">>>> invoking: {} on bean: {} with arguments: {} for exchange: {}", new Object[]{method, pojo, asString(arguments), exchange}); 276 } 277 Object result = invoke(method, pojo, arguments, exchange); 278 279 if (recipientList != null) { 280 // ensure its started 281 if (!recipientList.isStarted()) { 282 ServiceHelper.startService(recipientList); 283 } 284 return recipientList.sendToRecipientList(exchange, result, callback); 285 } 286 if (routingSlip != null) { 287 if (!routingSlip.isStarted()) { 288 ServiceHelper.startService(routingSlip); 289 } 290 return routingSlip.doRoutingSlip(exchange, result, callback); 291 } 292 293 // if the method returns something then set the value returned on the Exchange 294 if (!getMethod().getReturnType().equals(Void.TYPE) && result != Void.TYPE) { 295 if (exchange.getPattern().isOutCapable()) { 296 // force out creating if not already created (as its lazy) 297 LOG.debug("Setting bean invocation result on the OUT message: {}", result); 298 exchange.getOut().setBody(result); 299 // propagate headers 300 exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders()); 301 } else { 302 // if not out then set it on the in 303 LOG.debug("Setting bean invocation result on the IN message: {}", result); 304 exchange.getIn().setBody(result); 305 } 306 } 307 308 // we did not use any of the eips, but just invoked the bean 309 // so notify the callback we are done synchronously 310 callback.done(true); 311 return true; 312 } 313 314 public Object getThis() { 315 return pojo; 316 } 317 318 public AccessibleObject getStaticPart() { 319 return method; 320 } 321 }; 322 } 323 324 public Class<?> getType() { 325 return type; 326 } 327 328 public Method getMethod() { 329 return method; 330 } 331 332 /** 333 * Returns the {@link org.apache.camel.ExchangePattern} that should be used when invoking this method. This value 334 * defaults to {@link org.apache.camel.ExchangePattern#InOut} unless some {@link org.apache.camel.Pattern} annotation is used 335 * to override the message exchange pattern. 336 * 337 * @return the exchange pattern to use for invoking this method. 338 */ 339 public ExchangePattern getPattern() { 340 return pattern; 341 } 342 343 public Expression getParametersExpression() { 344 return parametersExpression; 345 } 346 347 public List<ParameterInfo> getBodyParameters() { 348 return bodyParameters; 349 } 350 351 public Class<?> getBodyParameterType() { 352 if (bodyParameters.isEmpty()) { 353 return null; 354 } 355 ParameterInfo parameterInfo = bodyParameters.get(0); 356 return parameterInfo.getType(); 357 } 358 359 public boolean bodyParameterMatches(Class<?> bodyType) { 360 Class<?> actualType = getBodyParameterType(); 361 return actualType != null && ObjectHelper.isAssignableFrom(bodyType, actualType); 362 } 363 364 public List<ParameterInfo> getParameters() { 365 return parameters; 366 } 367 368 public boolean hasBodyParameter() { 369 return !bodyParameters.isEmpty(); 370 } 371 372 public boolean hasCustomAnnotation() { 373 return hasCustomAnnotation; 374 } 375 376 public boolean hasHandlerAnnotation() { 377 return hasHandlerAnnotation; 378 } 379 380 public boolean isReturnTypeVoid() { 381 return method.getReturnType().getName().equals("void"); 382 } 383 384 public boolean isStaticMethod() { 385 return Modifier.isStatic(method.getModifiers()); 386 } 387 388 protected Object invoke(Method mth, Object pojo, Object[] arguments, Exchange exchange) throws InvocationTargetException { 389 try { 390 return mth.invoke(pojo, arguments); 391 } catch (IllegalAccessException e) { 392 throw new RuntimeExchangeException("IllegalAccessException occurred invoking method: " + mth + " using arguments: " + Arrays.asList(arguments), exchange, e); 393 } catch (IllegalArgumentException e) { 394 throw new RuntimeExchangeException("IllegalArgumentException occurred invoking method: " + mth + " using arguments: " + Arrays.asList(arguments), exchange, e); 395 } 396 } 397 398 protected Expression createParametersExpression() { 399 final int size = parameters.size(); 400 LOG.trace("Creating parameters expression for {} parameters", size); 401 402 final Expression[] expressions = new Expression[size]; 403 for (int i = 0; i < size; i++) { 404 Expression parameterExpression = parameters.get(i).getExpression(); 405 expressions[i] = parameterExpression; 406 LOG.trace("Parameter #{} has expression: {}", i, parameterExpression); 407 } 408 return new Expression() { 409 @SuppressWarnings("unchecked") 410 public <T> T evaluate(Exchange exchange, Class<T> type) { 411 Object[] answer = new Object[size]; 412 Object body = exchange.getIn().getBody(); 413 boolean multiParameterArray = false; 414 if (exchange.getIn().getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY) != null) { 415 multiParameterArray = exchange.getIn().getHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY, Boolean.class); 416 } 417 418 // if there was an explicit method name to invoke, then we should support using 419 // any provided parameter values in the method name 420 String methodName = exchange.getIn().getHeader(Exchange.BEAN_METHOD_NAME, "", String.class); 421 // the parameter values is between the parenthesis 422 String methodParameters = ObjectHelper.between(methodName, "(", ")"); 423 // use an iterator to walk the parameter values 424 Iterator<?> it = null; 425 if (methodParameters != null) { 426 // split the parameters safely separated by comma, but beware that we can have 427 // quoted parameters which contains comma as well, so do a safe quote split 428 String[] parameters = StringQuoteHelper.splitSafeQuote(methodParameters, ','); 429 it = ObjectHelper.createIterator(parameters); 430 } 431 432 // remove headers as they should not be propagated 433 // we need to do this before the expressions gets evaluated as it may contain 434 // a @Bean expression which would by mistake read these headers. So the headers 435 // must be removed at this point of time 436 exchange.getIn().removeHeader(Exchange.BEAN_MULTI_PARAMETER_ARRAY); 437 exchange.getIn().removeHeader(Exchange.BEAN_METHOD_NAME); 438 439 for (int i = 0; i < size; i++) { 440 // grab the parameter value for the given index 441 Object parameterValue = it != null && it.hasNext() ? it.next() : null; 442 // and the expected parameter type 443 Class<?> parameterType = parameters.get(i).getType(); 444 // the value for the parameter to use 445 Object value = null; 446 447 if (multiParameterArray) { 448 // get the value from the array 449 value = ((Object[])body)[i]; 450 } else { 451 // prefer to use parameter value if given, as they override any bean parameter binding 452 // we should skip * as its a type placeholder to indicate any type 453 if (parameterValue != null && !parameterValue.equals("*")) { 454 // evaluate the parameter value binding 455 value = evaluateParameterValue(exchange, i, parameterValue, parameterType); 456 } 457 458 // use bean parameter binding, if still no value 459 Expression expression = expressions[i]; 460 if (value == null && expression != null) { 461 value = evaluateParameterBinding(exchange, expression, i, parameterType); 462 } 463 } 464 465 // remember the value to use 466 if (value != Void.TYPE) { 467 answer[i] = value; 468 } 469 } 470 471 return (T) answer; 472 } 473 474 /** 475 * Evaluate using parameter values where the values can be provided in the method name syntax. 476 * <p/> 477 * This methods returns accordingly: 478 * <ul> 479 * <li><tt>null</tt> - if not a parameter value</li> 480 * <li><tt>Void.TYPE</tt> - if an explicit null, forcing Camel to pass in <tt>null</tt> for that given parameter</li> 481 * <li>a non <tt>null</tt> value - if the parameter was a parameter value, and to be used</li> 482 * </ul> 483 * 484 * @since 2.9 485 */ 486 private Object evaluateParameterValue(Exchange exchange, int index, Object parameterValue, Class<?> parameterType) { 487 Object answer = null; 488 489 // convert the parameter value to a String 490 String exp = exchange.getContext().getTypeConverter().convertTo(String.class, exchange, parameterValue); 491 if (exp != null) { 492 // must trim first as there may be spaces between parameters 493 exp = exp.trim(); 494 // check if its a valid parameter value 495 boolean valid = BeanHelper.isValidParameterValue(exp); 496 497 if (!valid) { 498 // it may be a parameter type instead, and if so, then we should return null, 499 // as this method is only for evaluating parameter values 500 Boolean isClass = BeanHelper.isAssignableToExpectedType(exchange.getContext().getClassResolver(), exp, parameterType); 501 // the method will return a non null value if exp is a class 502 if (isClass != null) { 503 return null; 504 } 505 } 506 507 // use simple language to evaluate the expression, as it may use the simple language to refer to message body, headers etc. 508 Expression expression = null; 509 try { 510 expression = exchange.getContext().resolveLanguage("simple").createExpression(exp); 511 parameterValue = expression.evaluate(exchange, Object.class); 512 } catch (Exception e) { 513 throw new ExpressionEvaluationException(expression, "Cannot create/evaluate simple expression: " + exp 514 + " to be bound to parameter at index: " + index + " on method: " + getMethod(), exchange, e); 515 } 516 517 if (parameterValue != null) { 518 519 // special for explicit null parameter values (as end users can explicit indicate they want null as parameter) 520 // see method javadoc for details 521 if ("null".equals(parameterValue)) { 522 return Void.TYPE; 523 } 524 525 // the parameter value was not already valid, but since the simple language have evaluated the expression 526 // which may change the parameterValue, so we have to check it again to see if its now valid 527 exp = exchange.getContext().getTypeConverter().convertTo(String.class, parameterValue); 528 // String values from the simple language is always valid 529 if (!valid) { 530 // re validate if the parameter was not valid the first time (String values should be accepted) 531 valid = parameterValue instanceof String || BeanHelper.isValidParameterValue(exp); 532 } 533 534 if (valid) { 535 // we need to unquote String parameters, as the enclosing quotes is there to denote a parameter value 536 if (parameterValue instanceof String) { 537 parameterValue = StringHelper.removeLeadingAndEndingQuotes((String) parameterValue); 538 } 539 try { 540 // its a valid parameter value, so convert it to the expected type of the parameter 541 answer = exchange.getContext().getTypeConverter().mandatoryConvertTo(parameterType, parameterValue); 542 if (LOG.isTraceEnabled()) { 543 LOG.trace("Parameter #{} evaluated as: {} type: ", new Object[]{index, answer, ObjectHelper.type(answer)}); 544 } 545 } catch (NoTypeConversionAvailableException e) { 546 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 547 } 548 } 549 } 550 } 551 552 return answer; 553 } 554 555 /** 556 * Evaluate using classic parameter binding using the pre compute expression 557 */ 558 private Object evaluateParameterBinding(Exchange exchange, Expression expression, int index, Class<?> parameterType) { 559 Object answer = null; 560 561 // use object first to avoid type conversion so we know if there is a value or not 562 Object result = expression.evaluate(exchange, Object.class); 563 if (result != null) { 564 // we got a value now try to convert it to the expected type 565 try { 566 answer = exchange.getContext().getTypeConverter().mandatoryConvertTo(parameterType, result); 567 if (LOG.isTraceEnabled()) { 568 LOG.trace("Parameter #{} evaluated as: {} type: ", new Object[]{index, answer, ObjectHelper.type(answer)}); 569 } 570 } catch (NoTypeConversionAvailableException e) { 571 throw ObjectHelper.wrapCamelExecutionException(exchange, e); 572 } 573 } else { 574 LOG.trace("Parameter #{} evaluated as null", index); 575 } 576 577 return answer; 578 } 579 580 @Override 581 public String toString() { 582 return "ParametersExpression: " + Arrays.asList(expressions); 583 } 584 585 }; 586 } 587 588 /** 589 * Finds the oneway annotation in priority order; look for method level annotations first, then the class level annotations, 590 * then super class annotations then interface annotations 591 * 592 * @param method the method on which to search 593 * @return the first matching annotation or none if it is not available 594 */ 595 protected Pattern findOneWayAnnotation(Method method) { 596 Pattern answer = getPatternAnnotation(method); 597 if (answer == null) { 598 Class<?> type = method.getDeclaringClass(); 599 600 // create the search order of types to scan 601 List<Class<?>> typesToSearch = new ArrayList<Class<?>>(); 602 addTypeAndSuperTypes(type, typesToSearch); 603 Class<?>[] interfaces = type.getInterfaces(); 604 for (Class<?> anInterface : interfaces) { 605 addTypeAndSuperTypes(anInterface, typesToSearch); 606 } 607 608 // now let's scan for a type which the current declared class overloads 609 answer = findOneWayAnnotationOnMethod(typesToSearch, method); 610 if (answer == null) { 611 answer = findOneWayAnnotation(typesToSearch); 612 } 613 } 614 return answer; 615 } 616 617 /** 618 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 619 * on an annotation which is also annotated 620 * 621 * @param annotatedElement the element to look for the annotation 622 * @return the first matching annotation or null if none could be found 623 */ 624 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement) { 625 return getPatternAnnotation(annotatedElement, 2); 626 } 627 628 /** 629 * Returns the pattern annotation on the given annotated element; either as a direct annotation or 630 * on an annotation which is also annotated 631 * 632 * @param annotatedElement the element to look for the annotation 633 * @param depth the current depth 634 * @return the first matching annotation or null if none could be found 635 */ 636 protected Pattern getPatternAnnotation(AnnotatedElement annotatedElement, int depth) { 637 Pattern answer = annotatedElement.getAnnotation(Pattern.class); 638 int nextDepth = depth - 1; 639 640 if (nextDepth > 0) { 641 // look at all the annotations to see if any of those are annotated 642 Annotation[] annotations = annotatedElement.getAnnotations(); 643 for (Annotation annotation : annotations) { 644 Class<? extends Annotation> annotationType = annotation.annotationType(); 645 if (annotation instanceof Pattern || annotationType.equals(annotatedElement)) { 646 continue; 647 } else { 648 Pattern another = getPatternAnnotation(annotationType, nextDepth); 649 if (pattern != null) { 650 if (answer == null) { 651 answer = another; 652 } else { 653 LOG.warn("Duplicate pattern annotation: " + another + " found on annotation: " + annotation + " which will be ignored"); 654 } 655 } 656 } 657 } 658 } 659 return answer; 660 } 661 662 /** 663 * Adds the current class and all of its base classes (apart from {@link Object} to the given list 664 */ 665 protected void addTypeAndSuperTypes(Class<?> type, List<Class<?>> result) { 666 for (Class<?> t = type; t != null && t != Object.class; t = t.getSuperclass()) { 667 result.add(t); 668 } 669 } 670 671 /** 672 * Finds the first annotation on the base methods defined in the list of classes 673 */ 674 protected Pattern findOneWayAnnotationOnMethod(List<Class<?>> classes, Method method) { 675 for (Class<?> type : classes) { 676 try { 677 Method definedMethod = type.getMethod(method.getName(), method.getParameterTypes()); 678 Pattern answer = getPatternAnnotation(definedMethod); 679 if (answer != null) { 680 return answer; 681 } 682 } catch (NoSuchMethodException e) { 683 // ignore 684 } 685 } 686 return null; 687 } 688 689 690 /** 691 * Finds the first annotation on the given list of classes 692 */ 693 protected Pattern findOneWayAnnotation(List<Class<?>> classes) { 694 for (Class<?> type : classes) { 695 Pattern answer = getPatternAnnotation(type); 696 if (answer != null) { 697 return answer; 698 } 699 } 700 return null; 701 } 702 703 protected boolean hasExceptionParameter() { 704 for (ParameterInfo parameter : parameters) { 705 if (Exception.class.isAssignableFrom(parameter.getType())) { 706 return true; 707 } 708 } 709 return false; 710 } 711 712 }