Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interceptor class visibility in Byte Buddy

Byte Buddy seems to only appreciate public classes as interceptor implementations even if I provide the actual instance; frequently I find myself wanting to do something like this:

import static MethodDelegation.to;

new ByteBuddy().subclass(Object.class).method(any()).intercept(to(new Object() {
  @RuntimeType
  public Object intercept(@Origin Method m, @AllArguments Object[] a) {
    return null;
  }
});

Which however results in an exception as follows:

Exception in thread "main" java.lang.IllegalStateException: class net.bytebuddy.renamed.java.lang.Object$ByteBuddy$pUmdGhyP cannot access class us.levk.guice.vs.Scopes$1Builder$1

Is there any reason behind visibility enforcement?

EDIT: I'm still having trouble with it though; I'm getting a different exception, this is my code:

package us.levk.guice.vs;

import static net.bytebuddy.implementation.MethodDelegation.to;
import static net.bytebuddy.matcher.ElementMatchers.any;

import java.lang.reflect.Method;
import java.util.function.Function;
import java.util.function.Supplier;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;

public class Bar {

  Function<Supplier<?>, Class<?>> wrapper () {
    return s -> {
      return new ByteBuddy ().subclass (Object.class)
                             .name (Bar.class.getPackage ().getName () + ".Foo")
                             .method (any ())
                             .intercept (to (new Object () {
                               @RuntimeType
                               public Object intercept (@Origin Method method,
                                                        @AllArguments Object[] args) {
                                 System.out.println (method);
                                 return null;
                               }
                             }))
                             .make ()
                             .load (getClass ().getClassLoader (),
                                    ClassLoadingStrategy.Default.WRAPPER)
                             .getLoaded ();
    };
  }

  public static void main (String[] args) throws InstantiationException, IllegalAccessException {
    new Bar ().wrapper ().apply ( () -> new Integer (1)).newInstance ().toString ();
  }
}

And the exception is now:

Exception in thread "main" java.lang.IllegalAccessError: tried to access class us.levk.guice.vs.Bar$1 from class us.levk.guice.vs.Foo

EDIT2:

It works fine if I change the classloading strategy to INJECTION

like image 297
Lev Kuznetsov Avatar asked Jan 13 '16 00:01

Lev Kuznetsov


1 Answers

In this case, Byte Buddy informs you about a visibility constraint that would cause an IllegalAccessException at runtime.

Eventhough you declare your method public, the anonymous class is automatically defined to be package private by javac. Therefore, the method is not visible to the generated class since net.bytebuddy.renamed.java.lang.Object is not equal to us.levk.guice.vs.Scopes.

If you did:

new ByteBuddy().subclass(Object.class).name("us.levk.guice.vs.Foo")
               .method(any()).intercept(to(new Object() {
  @RuntimeType
  public Object intercept(@Origin Method m, @AllArguments Object[] a) {
    return null;
  }
}));

everything would work. Alternatively, you can define - for example - a public interface such as:

public interface Foo {
  @RuntimeType
  Object intercept(@Origin Method m, @AllArguments Object[] a);
}

new ByteBuddy().subclass(Object.class).name("us.levk.guice.vs.Foo")
               .method(any()).intercept(to(new Foo() {
  @RuntimeType
  public Object intercept(@Origin Method m, @AllArguments Object[] a) {
    return null;
  }
}, Foo.class));
like image 72
Rafael Winterhalter Avatar answered Oct 19 '22 16:10

Rafael Winterhalter