Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito, Java 9 and java.lang.ClassNotFoundException: sun.reflect.ReflectionFactory

My project is a Wildfly 13 application which uses Mockito testing library. The app is not using Java 9 module structure. As long as the server ran on Java 8 the tests worked fine, but once we upgraded to Java 9 they failed with the following exception:

org.objenesis.ObjenesisException: java.lang.ClassNotFoundException: sun.reflect.ReflectionFactory from [Module "test.war" from Service Module Loader]
at test.war//org.objenesis.instantiator.sun.SunReflectionFactoryHelper.getReflectionFactoryClass(SunReflectionFactoryHelper.java:63)
at test.war//org.objenesis.instantiator.sun.SunReflectionFactoryHelper.newConstructorForSerialization(SunReflectionFactoryHelper.java:37)
at test.war//org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.<init>(SunReflectionFactoryInstantiator.java:41)
at test.war//org.objenesis.strategy.StdInstantiatorStrategy.newInstantiatorOf(StdInstantiatorStrategy.java:68)
at test.war//org.objenesis.ObjenesisBase.getInstantiatorOf(ObjenesisBase.java:94)
at test.war//org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
at test.war//org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:19)
at test.war//org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:47)
at test.war//org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
at test.war//org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
at test.war//org.mockito.internal.MockitoCore.mock(MockitoCore.java:68)
at test.war//org.mockito.Mockito.mock(Mockito.java:1895)
at test.war//org.mockito.Mockito.mock(Mockito.java:1804)
at test.war//application code...

I tried all kinds of options and combinations:

  • compiling with Java 8, 9, 10
  • running with the latest versions of OpenJDK 9 and 10
  • setting compiler and vm options --add-modules jdk.unsupported and --add-exports jdk.unsupported/sun.reflect=ALL-UNNAMED"

Always the same error. I'm out of ideas.

Result of --show-module-resolution

root jdk.javadoc jrt:/jdk.javadoc
root jdk.jdi jrt:/jdk.jdi
root jdk.jshell jrt:/jdk.jshell
root jdk.sctp jrt:/jdk.sctp
root jdk.xml.dom jrt:/jdk.xml.dom
root jdk.jsobject jrt:/jdk.jsobject
root jdk.unsupported jrt:/jdk.unsupported
root jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
root jdk.httpserver jrt:/jdk.httpserver
root jdk.management jrt:/jdk.management
root jdk.net jrt:/jdk.net
root jdk.security.auth jrt:/jdk.security.auth
root jdk.dynalink jrt:/jdk.dynalink
root java.se jrt:/java.se
root jdk.compiler jrt:/jdk.compiler
root jdk.accessibility jrt:/jdk.accessibility
root jdk.jartool jrt:/jdk.jartool
root jdk.jconsole jrt:/jdk.jconsole
root jdk.attach jrt:/jdk.attach
root jdk.security.jgss jrt:/jdk.security.jgss
jdk.security.jgss requires java.logging jrt:/java.logging
jdk.security.jgss requires java.security.sasl jrt:/java.security.sasl
jdk.security.jgss requires java.security.jgss jrt:/java.security.jgss
jdk.attach requires jdk.internal.jvmstat jrt:/jdk.internal.jvmstat
jdk.jconsole requires jdk.attach jrt:/jdk.attach
jdk.jconsole requires java.rmi jrt:/java.rmi
jdk.jconsole requires jdk.management jrt:/jdk.management
jdk.jconsole requires java.management jrt:/java.management
jdk.jconsole requires jdk.management.agent jrt:/jdk.management.agent
jdk.jconsole requires java.desktop jrt:/java.desktop
jdk.jconsole requires jdk.internal.jvmstat jrt:/jdk.internal.jvmstat
jdk.jconsole requires java.management.rmi jrt:/java.management.rmi
jdk.accessibility requires java.desktop jrt:/java.desktop
jdk.compiler requires java.compiler jrt:/java.compiler
java.se requires java.datatransfer jrt:/java.datatransfer
java.se requires java.compiler jrt:/java.compiler
java.se requires java.scripting jrt:/java.scripting
java.se requires java.desktop jrt:/java.desktop
java.se requires java.security.sasl jrt:/java.security.sasl
java.se requires java.naming jrt:/java.naming
java.se requires java.sql.rowset jrt:/java.sql.rowset
java.se requires java.security.jgss jrt:/java.security.jgss
java.se requires java.sql jrt:/java.sql
java.se requires java.management.rmi jrt:/java.management.rmi
java.se requires java.management jrt:/java.management
java.se requires java.xml.crypto jrt:/java.xml.crypto
java.se requires java.instrument jrt:/java.instrument
java.se requires java.rmi jrt:/java.rmi
java.se requires java.prefs jrt:/java.prefs
java.se requires java.logging jrt:/java.logging
java.se requires java.xml jrt:/java.xml
jdk.dynalink requires java.logging jrt:/java.logging
jdk.security.auth requires java.naming jrt:/java.naming
jdk.security.auth requires java.security.jgss jrt:/java.security.jgss
jdk.management requires java.management jrt:/java.management
jdk.scripting.nashorn requires java.logging jrt:/java.logging
jdk.scripting.nashorn requires java.scripting jrt:/java.scripting
jdk.scripting.nashorn requires jdk.dynalink jrt:/jdk.dynalink
jdk.jsobject requires java.desktop jrt:/java.desktop
jdk.xml.dom requires java.xml jrt:/java.xml
jdk.jshell requires java.compiler jrt:/java.compiler
jdk.jshell requires jdk.compiler jrt:/jdk.compiler
jdk.jshell requires java.logging jrt:/java.logging
jdk.jshell requires jdk.internal.opt jrt:/jdk.internal.opt
jdk.jshell requires jdk.internal.le jrt:/jdk.internal.le
jdk.jshell requires jdk.jdi jrt:/jdk.jdi
jdk.jshell requires java.prefs jrt:/java.prefs
jdk.jshell requires jdk.internal.ed jrt:/jdk.internal.ed
jdk.jdi requires jdk.attach jrt:/jdk.attach
jdk.jdi requires jdk.jdwp.agent jrt:/jdk.jdwp.agent
jdk.javadoc requires java.xml jrt:/java.xml
jdk.javadoc requires java.compiler jrt:/java.compiler
jdk.javadoc requires jdk.compiler jrt:/jdk.compiler
java.security.sasl requires java.logging jrt:/java.logging
java.security.jgss requires java.naming jrt:/java.naming
java.rmi requires java.logging jrt:/java.logging
jdk.management.agent requires java.management jrt:/java.management
jdk.management.agent requires java.management.rmi jrt:/java.management.rmi
java.desktop requires java.xml jrt:/java.xml
java.desktop requires java.prefs jrt:/java.prefs
java.desktop requires java.datatransfer jrt:/java.datatransfer
java.management.rmi requires java.naming jrt:/java.naming
java.management.rmi requires java.management jrt:/java.management
java.management.rmi requires java.rmi jrt:/java.rmi
java.naming requires java.security.sasl jrt:/java.security.sasl
java.sql.rowset requires java.logging jrt:/java.logging
java.sql.rowset requires java.naming jrt:/java.naming
java.sql.rowset requires java.sql jrt:/java.sql
java.sql requires java.logging jrt:/java.logging
java.sql requires java.xml jrt:/java.xml
java.xml.crypto requires java.logging jrt:/java.logging
java.xml.crypto requires java.xml jrt:/java.xml
java.prefs requires java.xml jrt:/java.xml
java.management binds java.management.rmi jrt:/java.management.rmi
java.management binds jdk.management jrt:/jdk.management
jdk.jshell binds jdk.editpad jrt:/jdk.editpad
java.naming binds jdk.naming.dns jrt:/jdk.naming.dns
java.naming binds jdk.naming.rmi jrt:/jdk.naming.rmi
java.datatransfer binds java.desktop jrt:/java.desktop
jdk.dynalink binds jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
java.base binds jdk.charsets jrt:/jdk.charsets
java.base binds java.security.jgss jrt:/java.security.jgss
java.base binds java.security.sasl jrt:/java.security.sasl
java.base binds java.naming jrt:/java.naming
java.base binds jdk.security.jgss jrt:/jdk.security.jgss
java.base binds java.smartcardio jrt:/java.smartcardio
java.base binds jdk.crypto.ec jrt:/jdk.crypto.ec
java.base binds jdk.crypto.cryptoki jrt:/jdk.crypto.cryptoki
java.base binds java.xml.crypto jrt:/java.xml.crypto
java.base binds java.desktop jrt:/java.desktop
java.base binds jdk.localedata jrt:/jdk.localedata
java.base binds jdk.jlink jrt:/jdk.jlink
java.base binds jdk.jdeps jrt:/jdk.jdeps
java.base binds jdk.compiler jrt:/jdk.compiler
java.base binds jdk.jartool jrt:/jdk.jartool
java.base binds jdk.javadoc jrt:/jdk.javadoc
java.base binds jdk.zipfs jrt:/jdk.zipfs
java.base binds java.management jrt:/java.management
java.base binds jdk.security.auth jrt:/jdk.security.auth
java.base binds java.logging jrt:/java.logging
java.compiler binds jdk.compiler jrt:/jdk.compiler
java.compiler binds jdk.javadoc jrt:/jdk.javadoc
jdk.internal.jvmstat binds jdk.jstatd jrt:/jdk.jstatd
java.scripting binds jdk.scripting.nashorn jrt:/jdk.scripting.nashorn
jdk.jstatd requires java.rmi jrt:/java.rmi
jdk.jstatd requires jdk.internal.jvmstat jrt:/jdk.internal.jvmstat
jdk.jdeps requires java.compiler jrt:/java.compiler
jdk.jdeps requires jdk.compiler jrt:/jdk.compiler
jdk.jlink requires jdk.jdeps jrt:/jdk.jdeps
jdk.jlink requires jdk.internal.opt jrt:/jdk.internal.opt
jdk.crypto.cryptoki requires jdk.crypto.ec jrt:/jdk.crypto.ec
jdk.naming.rmi requires java.naming jrt:/java.naming
jdk.naming.rmi requires java.rmi jrt:/java.rmi
jdk.naming.dns requires java.naming jrt:/java.naming
jdk.editpad requires java.desktop jrt:/java.desktop
jdk.editpad requires jdk.internal.ed jrt:/jdk.internal.ed
like image 352
Jure Vizjak Avatar asked Jul 26 '18 14:07

Jure Vizjak


2 Answers

With more digging I found the solution at https://developer.jboss.org/thread/278334 which pointed me to https://docs.jboss.org/author/display/WFLY10/Class+Loading+in+WildFly. The article has a section titled "Accessing JDK classes" which states that not all classes are available to deployment by default, and you need to add them to jboss-deployment-structure.xml to make them available.

In my case:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
    <deployment>
        <dependencies>
            <system export="true">
                <paths>
                    <path name="sun/reflect"/>
                </paths>
            </system>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

This solution works for

  • Wildfly 13, running on Java 10.0.2 (9.0.4 worked too)
  • app compiled with Java 8
  • Mockito 1.10.19 (I tried 2.20.0 but there were new errors)
like image 145
Jure Vizjak Avatar answered Nov 17 '22 17:11

Jure Vizjak


As of Java 11, WildFly 14+ and Mockito 2.23.0+, adding jdk.unsupported module dependency to your WAR solves the problem. You can do this in two ways:

A. META-INF/MANIFEST.MF

Add the following line to META_INF/MANIFEST.MF of your WAR archive:

Dependencies: jdk.unsupported

Note that the file must end with a new line or carriage return.

Here is how you'd typically do it with ShrinkWrap/Arquillian:

@Deployment
public static WebArchive createDeployment() {
    return ShrinkWrap.create(WebArchive.class, "my-app.war")
        .addPackages(true, Mockito.class.getPackage(), Objenesis.class.getPackage(), ByteBuddy.class.getPackage())
        .addAsManifestResource(new StringAsset("Dependencies: jdk.unsupported\n" /* required by Mockito */), "MANIFEST.MF");
}

B. WEB-INF/jboss-deployment-structure.xml

Add the following content to WEB-INF/jboss-deployment-structure.xml of your WAR archive:

<jboss-deployment-structure>
  <deployment>
    <dependencies>
      <module name="jdk.unsupported" />
    </dependencies>
  </deployment>
</jboss-deployment-structure>
like image 44
Peter Palaga Avatar answered Nov 17 '22 19:11

Peter Palaga