1

I would like to use a dynamic proxy using Proxy.newProxyInstance for a generated classes (Swagger) and these are not implementing an interface, which brought me to ByteBuddy, using which it should allow me to build the interface required by Proxy.newProxyInstance.
I got so far:

    Class<?> restClass = RestServiceApi.class;
    Builder<?> builder = byteBuddy.makeInterface().merge(Visibility.PUBLIC).name(restClass.getName() + "I");
    for (Method method : restClass.getMethods()) {

builder = builder.defineMethod(method.getName(),HOW_TO_DO_THIS);

        }
        Class<?> restInterface = builder.make().load(this.getClass().getClassLoader()).getLoaded();
        Class<?>[] proxyInterfaces = new Class<?>[] { restInterface };
// TODO create manipulatedRestServiceApiThatImplementsRestInterfaceI
        asyncServiceProxy = new AsynchronousServiceProxy<>(RestServiceApi.class, errorHandler, guiBlockingListener);
        ctrAsyncService = (manipulatedRestServiceApiThatImplementsRestInterfaceI) Proxy.newProxyInstance(RestServiceApi.class.getClassLoader(), proxyInterfaces, asyncServiceProxy);

Even if can figure out, how to write the code required to to define each method, I get the feeling, that I'm not doing it in the correct way, that I'm going to write a massive amount of code to translate the reflection information of Method to whatever ByteBudd requires and I suspect there is an easier way to build this interface class.

I would very much appreciate if anyone can point me into the correct direction.

  • Have you considered customising swagger code generator? – kan Jan 27 '21 at 23:56
  • This dynamically created interface will be not related to original class, so cast `(RestServiceApi)` will fail. Not sure how do you expect it could work. – kan Jan 28 '21 at 00:10
  • Your are right; I edited the question accordingly. – Christoph Nievergelt Jan 28 '21 at 05:41
  • Whatever `manipulatedRestServiceApiThatImplementsRestInterfaceI` means, it makes little sense to me. `ctrAsyncService` has no type at compile time. How would you use it later? – kan Jan 29 '21 at 00:03
  • Maybe this is what you want? https://stackoverflow.com/questions/40292185/how-to-create-a-dynamic-proxy-using-bytebuddy – kan Jan 29 '21 at 00:07

2 Answers2

2

Turned out that my hunch was correct (I was going at it from the wrong angle) and should have asked the question more broadly. What I actually needed (but didn't ask) was a solution to building dynamic proxies where you don't have a service interface but a service class.
With a service interface, you can use java.lang.reflect.Proxy to build a dynamic service proxy:

MyInvocationHandler<ServiceInterface> serviceProxy = new MyInvocationHandler<>();
proxiedService = (ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), new Class[] { ServiceInterface.class }, serviceProxy);

proxiedService.methodOfServiceInterface();

The statement proxiedService.methodOfServiceInterface() and will call the method MyInvocationHandler.invoke(Object proxy, final Method method, final Object[] arguments) which can do whatever is needed to execute the actual method (e.g. via remote method invocation).
This approach works fine to call EJBs or SOAP services where you have an interface, but breaks with a service class. So what I actually needed was an algorithm to build a dynamic service proxy that works for both service interfaces and service classes.
Building the dynamic service proxy with ByteBuddy was actually quite simple. The following generic method implements the required algorithm:

    /**
     * Creates a service proxy to call methods of the service class/interface.
     *
     * @param <T>                     type of service class or interface
     * @param serviceClassOrInterface service class or interface
     * @param serviceProxy            the service proxy
     * @return service proxy
     */
    public static <T> T createDynamicProxy(Class<T> serviceClassOrInterface, MyInvocationHandler<T> serviceProxy) {
        try {
            return new ByteBuddy()
                .subclass(serviceClassOrInterface)
                .method(isDeclaredBy(serviceClassOrInterface))
                .intercept(InvocationHandlerAdapter.of(serviceProxy))
                .make()
                .load(serviceClassOrInterface.getClassLoader())
                .getLoaded().getConstructor().newInstance();
        } catch (Exception exception) {
            throw new RuntimeException("Error creating dynamic proxy of " + serviceClassOrInterface.getName(), exception);
        }
    }

which can be used like this:

MyInvocationHandler<ServiceInterface> serviceProxy = new MyInvocationHandler<>();
proxiedService = createDynamicProxy(serviceInterface, serviceProxy);
1

You can simply apply:

builder = builder.define(method).intercept(MethodDelegation.to(YourDelegation.class));

MethodDelegation is very extensively documented in the Javadoc, you would probably need to resolve the invoked method via it's signature and then call it using reflection, for the simplest solution.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • Sorry about my quip regarding documentation; turn's out that I missed that the sources had not been downloaded and I was just seeing decompiled code; I removed it. – Christoph Nievergelt Jan 30 '21 at 13:34