Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cant mock class if its classloader cant load ClassMockingData #504

Open
trancexpress opened this issue Dec 13, 2023 · 0 comments
Open

Cant mock class if its classloader cant load ClassMockingData #504

trancexpress opened this issue Dec 13, 2023 · 0 comments
Assignees
Labels
Milestone

Comments

@trancexpress
Copy link

trancexpress commented Dec 13, 2023

To reproduce:

package test;

import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;

public class Test {
    public static void main(String[] args) throws Exception {
        URL url = Paths.get(System.getProperty("user.dir")).toUri().toURL();
        URLClassLoader cl = new URLClassLoader(new URL[] { url }, null);
        Class<?> test = cl.loadClass("test.Test");
        Object a = org.easymock.EasyMock.createStrictControl().createMock(test);
        System.out.println(a);
    }
}

This snippet goes in a folder test, next to this folder I have easymock-5.2.0.jar and objenesis-3.3.jar.

From the root folder containing the folder test and the libraries, compile with:

/usr/lib/jvm/java-21/bin/javac -cp objenesis-3.3.jar:easymock-5.2.0.jar test/Test.java

Run with:

/usr/lib/jvm/java-21/bin/java -cp objenesis-3.3.jar:easymock-5.2.0.jar:. test.Test

Observe error:

Exception in thread "main" java.lang.RuntimeException: java.lang.IllegalAccessException: no such field: test.Test$$$EasyMock$1.$callback/org.easymock.internal.ClassMockingData/putField
        at org.easymock.internal.ClassProxyFactory.getCallbackSetter(ClassProxyFactory.java:282)
        at org.easymock.internal.ClassProxyFactory.createProxy(ClassProxyFactory.java:213)
        at org.easymock.internal.MocksControl.createMock(MocksControl.java:110)
        at org.easymock.internal.MocksControl.createMock(MocksControl.java:83)
        at test.Test.main(Test.java:12)
Caused by: java.lang.IllegalAccessException: no such field: test.Test$$$EasyMock$1.$callback/org.easymock.internal.ClassMockingData/putField
        at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:911)
        at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:994)
        at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3742)
        at java.base/java.lang.invoke.MethodHandles$Lookup.findSetter(MethodHandles.java:3144)
        at org.easymock.internal.ClassProxyFactory.getCallbackSetter(ClassProxyFactory.java:280)
        ... 4 more
Caused by: java.lang.LinkageError: bad field type alias: class org.easymock.internal.ClassMockingData not visible from class test.Test$$$EasyMock$1
        at java.base/java.lang.invoke.MemberName.checkForTypeAlias(MemberName.java:825)
        at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:966)
        at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:991)
        ... 7 more

Seen both with JDK 21 and JDK 17.

I don't see the error if I use easymock-5.0.1.jar, unfortunately with 5.0.1 and JDK 21 I run into another issue:

package test;                                                                                                                                                                                                                                
public class Test {                                                                                                                                                                                                                          
    public static void main(String[] args) {
        Object a = org.easymock.EasyMock.createStrictControl().createMock(Test.class);
        System.out.println(a);
    }
}
$ /usr/lib/jvm/java-21/bin/java -cp easymock-5.0.1.jar:objenesis-3.3.jar test/Test.java 
Exception in thread "main" java.lang.IllegalArgumentException: test.Test$$$EasyMock$1 must be defined in the same package as org.easymock.internal.ClassProxyFactory
        at org.easymock.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1635)
        at org.easymock.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:118)
        at org.easymock.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:519)
        at org.easymock.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
        at org.easymock.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6317)
        at org.easymock.internal.ClassProxyFactory.createProxy(ClassProxyFactory.java:121)
        at org.easymock.internal.MocksControl.createMock(MocksControl.java:108)
        at org.easymock.internal.MocksControl.createMock(MocksControl.java:81)
        at test.Test.main(Test.java:4)

Our use case is, using easymock 5.2.0 in Eclipse tests running on Java 17+. Due to OSGI classloading, each bundle can load classes from bundles listed in its MANIFEST.MF. We have a production bundle and want to mock a class in it. The production bundle doesn't depend on easymock and so cannot load easymock classes. The mocking then fails as seen above.

With easymock 5.1.0 we see both problems (mocking on Java 21 and mocking if classloader doesn't see easymock).

@henri-tremblay henri-tremblay self-assigned this Jun 9, 2024
@henri-tremblay henri-tremblay added this to the 5.4.0 milestone Jun 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants