user9323254
user9323254

Reputation: 11

Redefining an unloaded class with ByteBuddy

I am trying to redefine an unloaded class and get java.lang.IllegalStateException: Class already loaded. The code is taken straight from the tutorial, only had to append "getLoaded()" for it to compile, see below. ByteBuddy version is the latest available 1.12.10, Java version 1.8.0_202. I tried a few other JVMs too getting same results.

I see that ByteArrayClassLoader is loading the class using Launcher$AppClassLoader instead of self. This causes the exception.

What do I need to make it work?

Thank you

class MyApplication  {
    public static void main(String[] args) throws Exception {
        TypePool typePool = TypePool.Default.ofSystemLoader();
        Class bar = new ByteBuddy()
          .redefine(typePool.describe("foo.Bar").resolve(), // do not use 'Bar.class'
                    ClassFileLocator.ForClassLoader.ofSystemLoader())
          .defineField("qux", String.class) // we learn more about defining fields later
          .make()
          .load(ClassLoader.getSystemClassLoader()) .getLoaded();
        assertThat(bar.getDeclaredField("qux"), notNullValue());
    }
}

Exception in thread "main" java.lang.IllegalStateException: Class already loaded: class foo.Bar
    at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.load(ByteArrayClassLoader.java:362)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$WrappingDispatcher.load(ClassLoadingStrategy.java:367)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:148)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6166)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6154)
    at MyApplication.main(UnloadedTest.java:17)

Upvotes: 1

Views: 866

Answers (1)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44007

You are using a default class loading strategy which is one without use of Unsafe, but one that is creating a new class loader as a child of the supplied loader. This new loader will query its parent - the system class loader - first, which then loads Foo. After Foo is defined, it cannot be defined once again in the new child.

You can avoid this by injecting the class using an injection-based strategy like ClassLoadingStrategy.Default.INJECTION or a lookup-based one (ClassLoadingStrategy.UsingLookup). This way, no child loader is created but the altered class is injected into the system loader. Or you can disable querying the child loader by using ClassLoadingStrategy.Default.CHILD_FIRST where any regular reference to Foo in the reminder of the program will however load the original class in addition.

Upvotes: 1

Related Questions