class MethodDispatch {

  public Object invoke(Object target, List args, ExecutionContext executionContext)
      throws NameNotFoundException, QueryInvocationTargetException {
    Object[] argsArray = args.toArray();

    try {
      MethodInvocationAuthorizer authorizer = executionContext.getMethodInvocationAuthorizer();

      // CQs are generally executed on individual events, so caching is just an overhead.
      if (executionContext.isCqQueryContext()) {
        if (!authorizer.authorize(_method, target)) {
          throw new NotAuthorizedException(UNAUTHORIZED_STRING + _method.getName());
        }
      } else {
        // Try to use cached result so authorizer gets invoked only once per query.
        boolean authorizationResult;
        Boolean cachedResult = (Boolean) executionContext.cacheGet(_method);

        if (cachedResult != null) {
          // Use cached result.
          authorizationResult = cachedResult;
        } else {
          // First time, evaluate and cache result.
          authorizationResult = authorizer.authorize(_method, target);
          executionContext.cachePut(_method, authorizationResult);
        }

        if (!authorizationResult) {
          throw new NotAuthorizedException(UNAUTHORIZED_STRING + _method.getName());
        }
      }

      return _method.invoke(target, argsArray);
    } catch (IllegalAccessException e) {
      throw new NameNotFoundException(
          String.format("Method ' %s ' in class ' %s ' is not accessible to the query processor",
              _method.getName(), target.getClass().getName()),
          e);
    } catch (InvocationTargetException e) {
      // if targetException is Exception, wrap it, otherwise wrap the InvocationTargetException
      // itself
      Throwable t = e.getTargetException();
      if (t instanceof Exception)
        throw new QueryInvocationTargetException(t);
      throw new QueryInvocationTargetException(e);
    }
  }

  public  MethodDispatch(Class targetClass, String methodName, List argTypes);

}