Exemple #1
0
  private MethodInfo chooseMethodWithMatchingParameters(
      Exchange exchange, String parameters, Collection<MethodInfo> operationList)
      throws AmbiguousMethodCallException {
    // we have hardcoded parameters so need to match that with the given operations
    Iterator<?> it = ObjectHelper.createIterator(parameters);
    int count = 0;
    while (it.hasNext()) {
      it.next();
      count++;
    }

    List<MethodInfo> operations = new ArrayList<MethodInfo>();
    for (MethodInfo info : operationList) {
      if (info.getParameters().size() == count) {
        operations.add(info);
      }
    }

    if (operations.isEmpty()) {
      return null;
    } else if (operations.size() == 1) {
      return operations.get(0);
    }

    // okay we still got multiple operations, so need to match the best one
    List<MethodInfo> candidates = new ArrayList<MethodInfo>();
    for (MethodInfo info : operations) {
      it = ObjectHelper.createIterator(parameters);
      int index = 0;
      boolean matches = true;
      while (it.hasNext()) {
        String parameter = (String) it.next();
        Class<?> parameterType = BeanHelper.getValidParameterType(parameter);
        Class<?> expectedType = info.getParameters().get(index).getType();

        if (parameterType != null && expectedType != null) {
          if (!parameterType.isAssignableFrom(expectedType)) {
            matches = false;
            break;
          }
        }

        index++;
      }

      if (matches) {
        candidates.add(info);
      }
    }

    if (candidates.size() > 1) {
      MethodInfo answer = getSingleCovariantMethod(candidates);
      if (answer == null) {
        throw new AmbiguousMethodCallException(exchange, candidates);
      }
      return answer;
    }
    return candidates.size() == 1 ? candidates.get(0) : null;
  }
 /**
  * Returns true if this method is covariant with the specified method (this method may above or
  * below the specified method in the class hierarchy)
  */
 public boolean isCovariantWith(MethodInfo method) {
   return method.getMethod().getName().equals(this.getMethod().getName())
       && (method.getMethod().getReturnType().isAssignableFrom(this.getMethod().getReturnType())
           || this.getMethod()
               .getReturnType()
               .isAssignableFrom(method.getMethod().getReturnType()))
       && Arrays.deepEquals(
           method.getMethod().getParameterTypes(), this.getMethod().getParameterTypes());
 }
Exemple #3
0
 private MethodInfo getSingleCovariantMethod(Collection<MethodInfo> candidates) {
   // if all the candidates are actually covariant, it doesn't matter which one we call
   MethodInfo firstCandidate = candidates.iterator().next();
   for (MethodInfo candidate : candidates) {
     if (!firstCandidate.isCovariantWith(candidate)) {
       return null;
     }
   }
   return firstCandidate;
 }
Exemple #4
0
 private void removeNonMatchingMethods(List<MethodInfo> methods, String name) {
   Iterator<MethodInfo> it = methods.iterator();
   while (it.hasNext()) {
     MethodInfo info = it.next();
     if (!matchMethod(info.getMethod(), name)) {
       // method does not match so remove it
       it.remove();
     }
   }
 }
Exemple #5
0
 /**
  * Do we have a static method with the given name.
  *
  * <p>Shorthand method names for getters is supported, so you can pass in eg 'name' and Camel will
  * can find the real 'getName' method instead.
  *
  * @param methodName the method name
  * @return <tt>true</tt> if we have such a static method.
  */
 public boolean hasStaticMethod(String methodName) {
   List<MethodInfo> methods = getOperations(methodName);
   if (methods == null || methods.isEmpty()) {
     return false;
   }
   for (MethodInfo method : methods) {
     if (method.isStaticMethod()) {
       return true;
     }
   }
   return false;
 }
Exemple #6
0
  private MethodInfo chooseMethodWithMatchingBody(
      Exchange exchange,
      Collection<MethodInfo> operationList,
      List<MethodInfo> operationsWithCustomAnnotation)
      throws AmbiguousMethodCallException {
    // see if we can find a method whose body param type matches the message body
    Message in = exchange.getIn();
    Object body = in.getBody();
    if (body != null) {
      Class<?> bodyType = body.getClass();
      if (LOG.isTraceEnabled()) {
        LOG.trace(
            "Matching for method with a single parameter that matches type: {}",
            bodyType.getCanonicalName());
      }

      List<MethodInfo> possibles = new ArrayList<MethodInfo>();
      List<MethodInfo> possiblesWithException = new ArrayList<MethodInfo>();
      for (MethodInfo methodInfo : operationList) {
        // test for MEP pattern matching
        boolean out = exchange.getPattern().isOutCapable();
        if (out && methodInfo.isReturnTypeVoid()) {
          // skip this method as the MEP is Out so the method must return something
          continue;
        }

        // try to match the arguments
        if (methodInfo.bodyParameterMatches(bodyType)) {
          LOG.trace("Found a possible method: {}", methodInfo);
          if (methodInfo.hasExceptionParameter()) {
            // methods with accepts exceptions
            possiblesWithException.add(methodInfo);
          } else {
            // regular methods with no exceptions
            possibles.add(methodInfo);
          }
        }
      }

      // find best suited method to use
      return chooseBestPossibleMethodInfo(
          exchange,
          operationList,
          body,
          possibles,
          possiblesWithException,
          operationsWithCustomAnnotation);
    }

    // no match so return null
    return null;
  }
Exemple #7
0
 private void removeAllAbstractMethods(List<MethodInfo> methods) {
   Iterator<MethodInfo> it = methods.iterator();
   while (it.hasNext()) {
     MethodInfo info = it.next();
     // if the class is an interface then keep the method
     boolean isFromInterface =
         Modifier.isInterface(info.getMethod().getDeclaringClass().getModifiers());
     if (!isFromInterface && Modifier.isAbstract(info.getMethod().getModifiers())) {
       // we cannot invoke an abstract method
       it.remove();
     }
   }
 }
Exemple #8
0
 private static void removeAllSetterOrGetterMethods(List<MethodInfo> methods) {
   Iterator<MethodInfo> it = methods.iterator();
   while (it.hasNext()) {
     MethodInfo info = it.next();
     if (IntrospectionSupport.isGetter(info.getMethod())) {
       // skip getters
       it.remove();
     } else if (IntrospectionSupport.isSetter(info.getMethod())) {
       // skip setters
       it.remove();
     }
   }
 }
Exemple #9
0
  /**
   * Does the given method info override an existing method registered before (from a subclass)
   *
   * @param methodInfo the method to test
   * @return the already registered method to use, null if not overriding any
   */
  private MethodInfo overridesExistingMethod(MethodInfo methodInfo) {
    for (MethodInfo info : methodMap.values()) {
      Method source = info.getMethod();
      Method target = methodInfo.getMethod();

      boolean override = ObjectHelper.isOverridingMethod(source, target);
      if (override) {
        // same name, same parameters, then its overrides an existing class
        return info;
      }
    }

    return null;
  }
Exemple #10
0
  /**
   * Introspects the given method
   *
   * @param clazz the class
   * @param method the method
   * @return the method info, is newer <tt>null</tt>
   */
  private MethodInfo introspect(Class<?> clazz, Method method) {
    LOG.trace("Introspecting class: {}, method: {}", clazz, method);
    String opName = method.getName();

    MethodInfo methodInfo = createMethodInfo(clazz, method);

    // methods already registered should be preferred to use instead of super classes of existing
    // methods
    // we want to us the method from the sub class over super classes, so if we have already
    // registered
    // the method then use it (we are traversing upwards: sub (child) -> super (farther) )
    MethodInfo existingMethodInfo = overridesExistingMethod(methodInfo);
    if (existingMethodInfo != null) {
      LOG.trace(
          "This method is already overridden in a subclass, so the method from the sub class is preferred: {}",
          existingMethodInfo);
      return existingMethodInfo;
    }

    LOG.trace("Adding operation: {} for method: {}", opName, methodInfo);

    List<MethodInfo> existing = getOperations(opName);
    if (existing != null) {
      // we have an overloaded method so add the method info to the same key
      existing.add(methodInfo);
    } else {
      // its a new method we have not seen before so wrap it in a list and add it
      List<MethodInfo> methods = new ArrayList<MethodInfo>();
      methods.add(methodInfo);
      operations.put(opName, methods);
    }

    if (methodInfo.hasCustomAnnotation()) {
      operationsWithCustomAnnotation.add(methodInfo);
    } else if (methodInfo.hasBodyParameter()) {
      operationsWithBody.add(methodInfo);
    } else {
      operationsWithNoBody.add(methodInfo);
    }

    if (methodInfo.hasHandlerAnnotation()) {
      operationsWithHandlerAnnotation.add(methodInfo);
    }

    // must add to method map last otherwise we break stuff
    methodMap.put(method, methodInfo);

    return methodInfo;
  }
Exemple #11
0
 private MethodInfo chooseMethodWithCustomAnnotations(
     Exchange exchange, Collection<MethodInfo> possibles) throws AmbiguousMethodCallException {
   // if we have only one method with custom annotations let's choose that
   MethodInfo chosen = null;
   for (MethodInfo possible : possibles) {
     if (possible.hasCustomAnnotation()) {
       if (chosen != null) {
         chosen = null;
         break;
       } else {
         chosen = possible;
       }
     }
   }
   return chosen;
 }
Exemple #12
0
  private MethodInfo chooseBestPossibleMethodInfo(
      Exchange exchange,
      Collection<MethodInfo> operationList,
      Object body,
      List<MethodInfo> possibles,
      List<MethodInfo> possiblesWithException,
      List<MethodInfo> possibleWithCustomAnnotation)
      throws AmbiguousMethodCallException {

    Exception exception =
        ExpressionBuilder.exchangeExceptionExpression().evaluate(exchange, Exception.class);
    if (exception != null && possiblesWithException.size() == 1) {
      LOG.trace(
          "Exchange has exception set so we prefer method that also has exception as parameter");
      // prefer the method that accepts exception in case we have an exception also
      return possiblesWithException.get(0);
    } else if (possibles.size() == 1) {
      return possibles.get(0);
    } else if (possibles.isEmpty()) {
      LOG.trace("No possible methods so now trying to convert body to parameter types");

      // let's try converting
      Object newBody = null;
      MethodInfo matched = null;
      int matchCounter = 0;
      for (MethodInfo methodInfo : operationList) {
        if (methodInfo.getBodyParameterType() != null) {
          if (methodInfo.getBodyParameterType().isInstance(body)) {
            return methodInfo;
          }

          // we should only try to convert, as we are looking for best match
          Object value =
              exchange
                  .getContext()
                  .getTypeConverter()
                  .tryConvertTo(methodInfo.getBodyParameterType(), exchange, body);
          if (value != null) {
            if (LOG.isTraceEnabled()) {
              LOG.trace(
                  "Converted body from: {} to: {}",
                  body.getClass().getCanonicalName(),
                  methodInfo.getBodyParameterType().getCanonicalName());
            }
            matchCounter++;
            newBody = value;
            matched = methodInfo;
          }
        }
      }
      if (matchCounter > 1) {
        throw new AmbiguousMethodCallException(exchange, Arrays.asList(matched, matched));
      }
      if (matched != null) {
        LOG.trace("Setting converted body: {}", body);
        Message in = exchange.getIn();
        in.setBody(newBody);
        return matched;
      }
    } else {
      // if we only have a single method with custom annotations, let's use that one
      if (possibleWithCustomAnnotation.size() == 1) {
        MethodInfo answer = possibleWithCustomAnnotation.get(0);
        LOG.trace("There are only one method with annotations so we choose it: {}", answer);
        return answer;
      }
      // try to choose among multiple methods with annotations
      MethodInfo chosen = chooseMethodWithCustomAnnotations(exchange, possibles);
      if (chosen != null) {
        return chosen;
      }
      // just make sure the methods aren't all actually the same
      chosen = getSingleCovariantMethod(possibles);
      if (chosen != null) {
        return chosen;
      }
      throw new AmbiguousMethodCallException(exchange, possibles);
    }

    // cannot find a good method to use
    return null;
  }
Exemple #13
0
  private MethodInfo chooseMethodWithMatchingParameters(
      Exchange exchange, String parameters, Collection<MethodInfo> operationList)
      throws AmbiguousMethodCallException {
    // we have hardcoded parameters so need to match that with the given operations
    Iterator<?> it = ObjectHelper.createIterator(parameters);
    int count = 0;
    while (it.hasNext()) {
      it.next();
      count++;
    }

    List<MethodInfo> operations = new ArrayList<MethodInfo>();
    for (MethodInfo info : operationList) {
      if (info.getParameters().size() == count) {
        operations.add(info);
      }
    }

    if (operations.isEmpty()) {
      return null;
    } else if (operations.size() == 1) {
      return operations.get(0);
    }

    // okay we still got multiple operations, so need to match the best one
    List<MethodInfo> candidates = new ArrayList<MethodInfo>();
    MethodInfo fallbackCandidate = null;
    for (MethodInfo info : operations) {
      it = ObjectHelper.createIterator(parameters, ",", false);
      int index = 0;
      boolean matches = true;
      while (it.hasNext()) {
        String parameter = (String) it.next();
        if (parameter != null) {
          // must trim
          parameter = parameter.trim();
        }

        Class<?> parameterType = BeanHelper.getValidParameterType(parameter);
        Class<?> expectedType = info.getParameters().get(index).getType();

        if (parameterType != null && expectedType != null) {

          // if its a simple language then we need to evaluate the expression
          // so we have the result and can find out what type the parameter actually is
          if (StringHelper.hasStartToken(parameter, "simple")) {
            LOG.trace(
                "Evaluating simple expression for parameter #{}: {} to determine the class type of the parameter",
                index,
                parameter);
            Object out =
                getCamelContext()
                    .resolveLanguage("simple")
                    .createExpression(parameter)
                    .evaluate(exchange, Object.class);
            if (out != null) {
              parameterType = out.getClass();
            }
          }

          // skip java.lang.Object type, when we have multiple possible methods we want to avoid it
          // if possible
          if (Object.class.equals(expectedType)) {
            fallbackCandidate = info;
            matches = false;
            break;
          }

          boolean matchingTypes = isParameterMatchingType(parameterType, expectedType);
          if (!matchingTypes) {
            matches = false;
            break;
          }
        }

        index++;
      }

      if (matches) {
        candidates.add(info);
      }
    }

    if (candidates.size() > 1) {
      MethodInfo answer = getSingleCovariantMethod(candidates);
      if (answer != null) {
        return answer;
      }
    }
    return candidates.size() == 1 ? candidates.get(0) : fallbackCandidate;
  }
Exemple #14
0
  private MethodInvocation createInvocation(Object pojo, Exchange exchange, Method explicitMethod)
      throws AmbiguousMethodCallException, MethodNotFoundException {
    MethodInfo methodInfo = null;

    // find the explicit method to invoke
    if (explicitMethod != null) {
      for (List<MethodInfo> infos : operations.values()) {
        for (MethodInfo info : infos) {
          if (explicitMethod.equals(info.getMethod())) {
            return info.createMethodInvocation(pojo, exchange);
          }
        }
      }
      throw new MethodNotFoundException(exchange, pojo, explicitMethod.getName());
    }

    String methodName = exchange.getIn().getHeader(Exchange.BEAN_METHOD_NAME, String.class);
    if (methodName != null) {

      // do not use qualifier for name
      String name = methodName;
      if (methodName.contains("(")) {
        name = ObjectHelper.before(methodName, "(");
        // the must be a ending parenthesis
        if (!methodName.endsWith(")")) {
          throw new IllegalArgumentException(
              "Method should end with parenthesis, was " + methodName);
        }
      }
      boolean emptyParameters = methodName.endsWith("()");

      // special for getClass, as we want the user to be able to invoke this method
      // for example to log the class type or the likes
      if ("class".equals(name) || "getClass".equals(name)) {
        try {
          Method method = pojo.getClass().getMethod("getClass");
          methodInfo =
              new MethodInfo(
                  exchange.getContext(),
                  pojo.getClass(),
                  method,
                  Collections.<ParameterInfo>emptyList(),
                  Collections.<ParameterInfo>emptyList(),
                  false,
                  false);
        } catch (NoSuchMethodException e) {
          throw new MethodNotFoundException(exchange, pojo, "getClass");
        }
        // special for length on an array type
      } else if ("length".equals(name) && pojo.getClass().isArray()) {
        try {
          // need to use arrayLength method from ObjectHelper as Camel's bean OGNL support is method
          // invocation based
          // and not for accessing fields. And hence we need to create a MethodInfo instance with a
          // method to call
          // and therefore use arrayLength from ObjectHelper to return the array length field.
          Method method = ObjectHelper.class.getMethod("arrayLength", Object[].class);
          ParameterInfo pi =
              new ParameterInfo(
                  0,
                  Object[].class,
                  null,
                  ExpressionBuilder.mandatoryBodyExpression(Object[].class, true));
          List<ParameterInfo> lpi = new ArrayList<ParameterInfo>(1);
          lpi.add(pi);
          methodInfo =
              new MethodInfo(
                  exchange.getContext(), pojo.getClass(), method, lpi, lpi, false, false);
          // Need to update the message body to be pojo for the invocation
          exchange.getIn().setBody(pojo);
        } catch (NoSuchMethodException e) {
          throw new MethodNotFoundException(exchange, pojo, "getClass");
        }
      } else {
        List<MethodInfo> methods = getOperations(name);
        if (methods != null && methods.size() == 1) {
          // only one method then choose it
          methodInfo = methods.get(0);

          // validate that if we want an explicit no-arg method, then that's what we get
          if (emptyParameters && methodInfo.hasParameters()) {
            throw new MethodNotFoundException(exchange, pojo, methodName, "(with no parameters)");
          }
        } else if (methods != null) {
          // there are more methods with that name so we cannot decide which to use

          // but first let's try to choose a method and see if that complies with the name
          // must use the method name which may have qualifiers
          methodInfo = chooseMethod(pojo, exchange, methodName);

          // validate that if we want an explicit no-arg method, then that's what we get
          if (emptyParameters) {
            if (methodInfo == null || methodInfo.hasParameters()) {
              // we could not find a no-arg method with that name
              throw new MethodNotFoundException(exchange, pojo, methodName, "(with no parameters)");
            }
          }

          if (methodInfo == null
              || (name != null && !name.equals(methodInfo.getMethod().getName()))) {
            throw new AmbiguousMethodCallException(exchange, methods);
          }
        } else {
          // a specific method was given to invoke but not found
          throw new MethodNotFoundException(exchange, pojo, methodName);
        }
      }
    }

    if (methodInfo == null) {
      // no name or type
      methodInfo = chooseMethod(pojo, exchange, null);
    }
    if (methodInfo == null) {
      methodInfo = defaultMethod;
    }
    if (methodInfo != null) {
      LOG.trace("Chosen method to invoke: {} on bean: {}", methodInfo, pojo);
      return methodInfo.createMethodInvocation(pojo, exchange);
    }

    LOG.debug("Cannot find suitable method to invoke on bean: {}", pojo);
    return null;
  }