@Override
  public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
    final Callable<Object> taskCallable = () -> {
      try {
        return method.invoke(myTarget, args);
      }
      catch (IllegalAccessException | InvocationTargetException e) {
        LOG.error(e);
        return null;
      }
    };

    try {
      boolean immediateCall = method.getAnnotation(ImmediateCall.class) != null;

      boolean childCall = method.getAnnotation(ChildCall.class) != null;
      if (childCall) {
        Object child = immediateCall ? taskCallable.call() : executeAndWait(taskCallable);
        if (child == null) {
          return null;
        }

        Object result;
        Class<?> childClass = child.getClass();
        if (childClass.isArray()) {
          Class<?> componentType = childClass.getComponentType();
          int length = Array.getLength(child);
          result = Array.newInstance(componentType, length);
          for (int i = 0; i < length; i++) {
            Array.set(result, i, createChildProxy(Array.get(child, i)));
          }
        }
        else {
          result = createChildProxy(child);
        }

        return result;
      }

      if (immediateCall || StringUtil.equals(method.getName(), "toString")) {
        return taskCallable.call();
      }

      boolean asyncCall = method.getAnnotation(AsyncCall.class) != null;

      if (asyncCall) {
        myTaskExecutor.execute(() -> {
          try {
            taskCallable.call();
          }
          catch (Exception e) {
            LOG.error(e); // should never happen
          }
        });
        return null;
      }
      else {
        return executeAndWait(taskCallable);
      }
    }
    finally {
      boolean finalCall = method.getAnnotation(FinalCall.class) != null;
      if (finalCall) {
        myTaskExecutor.shutdownNow();
      }
    }
  }
@Override
  public Object invoke(Object proxy, final Method method, final Object[] args)