This exception occurs in a wide variety of scenarios when running an application on Java 9. Certain libraries and frameworks (Spring, Hibernate, JAXB) are particularly prone to it. Here's an example from Javassist:
java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1941a8ff
at java.base/jdk.internal.reflect.Reflection.throwInaccessibleObjectException(Reflection.java:427)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:201)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:192)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:186)
at javassist.util.proxy.SecurityActions.setAccessible(SecurityActions.java:102)
at javassist.util.proxy.FactoryHelper.toClass2(FactoryHelper.java:180)
at javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:163)
at javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:501)
at javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:486)
at javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:422)
at javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:394)
The message says:
Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1941a8ff
What can be done to avoid the exception and have the program run successfully?
The exception is caused by the Java Platform Module System that was introduced in Java 9, particularly its implementation of strong encapsulation. It only allows access under certain conditions, the most prominent ones are:
The same limitations are true for reflection, which the code causing the exception tried to use.
More precisely the exception is caused by a call to setAccessible
.
This can be seen in the stack trace above, where the corresponding lines in javassist.util.proxy.SecurityActions
look as follows:
static void setAccessible(final AccessibleObject ao,
final boolean accessible) {
if (System.getSecurityManager() == null)
ao.setAccessible(accessible); // <~ Dragons
else {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
ao.setAccessible(accessible); // <~ moar Dragons
return null;
}
});
}
}
To make sure the program runs successfully the module system must be convinced to allow access to the element on which setAccessible
was called.
All information required for that is contained in the exception message but there are a number of mechanisms to achieve this.
Which is the best one depends on the exact scenario that caused it.
Unable to make {member} accessible: module {A} does not 'opens {package}' to {B}
By far the most prominent scenarios are the following two:
A library or framework uses reflection to call into a JDK module. In this scenario:
{A}
is a Java module (prefixed with java.
or jdk.
){member}
and {package}
are parts of the Java API{B}
is a library, framework, or application module; often unnamed module @...
A reflection-based library/framework like Spring, Hibernate, JAXB, ... reflects over application code to access beans, entities,... In this scenario:
{A}
is an application module{member}
and {package}
are part of the application code{B}
is either a framework module or unnamed module @...
Note that some libraries (JAXB, for example) can fail on both accounts so have a close look at what scenario you're in! The one in the question is case 1.
The JDK modules are immutable for application developers so we can not change their properties. This leaves only one possible solution: command line flags. With them it is possible to open specific packages up for reflection.
So in a case like above (shortened)...
Unable to make java.lang.ClassLoader.defineClass accessible: module java.base does not "opens java.lang" to unnamed module @1941a8ff
... the correct fix is to launch the JVM as follows:
# --add-opens has the following syntax: {A}/{package}={B}
java --add-opens java.base/java.lang=ALL-UNNAMED
If the reflecting code is in a named module, ALL-UNNAMED
can be replaced by its name.
Note that it can sometimes be hard to find a way to apply this flag to the JVM that will actually execute the reflecting code. This can be particularly tough if the code in question is part of the project's build process and is executed in a JVM that the build tool spawned.
If there are too many flags to be added, you might consider using the encapsulation kill switch --permit-illegal-access
instead. It will allow all code on the class path to reflect overall named modules. Note that this flag will only work in Java 9!
In this scenario, it is likely that you can edit the module that reflection is used to break into.
(If not, you're effectively in case 1.) That means that command-line flags are not necessary and instead module {A}
's descriptor can be used to open up its internals.
There are a variety of choices:
exports {package}
, which makes it available at compile and run time to all codeexports {package} to {B}
, which makes it available at compile and run time but only to {B}
opens {package}
, which makes it available at run time (with or without reflection) to all codeopens {package} to {B}
, which makes it available at run time (with or without reflection) but only to {B}
open module {A} { ... }
, which makes all its packages available at run time (with or without reflection) to all codeSee this post for a more detailed discussion and comparison of these approaches.