java.lang.ExceptionInInitializerError with Java-16 | j.l.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module
Asked Answered
G

1

22

I have cglib as a transitive dependency in a Maven project. Despite adding what I believe to be the correct --add-opens I can't get the library to work with Java 16.

How do I get cglib to work with Java 16? I raised this issue on the github page.

Minimal reproducible example:

Main.java

import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        new Enhancer();
    }
}

With Java 15:

javac -cp cglib-3.3.0.jar Main.java

java --add-opens java.base/java.lang=ALL-UNNAMED -cp cglib-3.3.0.jar:asm-7.1.jar:. Main

Picked up _JAVA_OPTIONS: -Duser.language=en -Duser.country=GB
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by net.sf.cglib.core.ReflectUtils$1 (file:/Users/rbain/Desktop/cglib-3.3.0.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of net.sf.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

With Java 16:

javac -cp cglib-3.3.0.jar Main.java

java --add-opens java.base/java.lang=ALL-UNNAMED -cp cglib-3.3.0.jar:asm-7.1.jar:. Main

Exception in thread "main" java.lang.ExceptionInInitializerError
    at Main.main(Main.java:5)
Caused by: net.sf.cglib.core.CodeGenerationException: 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 @11739fa6
    at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:464)
    at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
    at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
    at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
    at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
    at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
    at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
    at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221)
    at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:174)
    at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:153)
    at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73)
    ... 1 more
Caused by: 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 @11739fa6
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:357)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
    at net.sf.cglib.core.ReflectUtils$1.run(ReflectUtils.java:61)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:554)
    at net.sf.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:52)
    at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:243)
    at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
    ... 13 more
Gabrielegabriell answered 6/4, 2021 at 18:57 Comment(7)
do you understand what Rafael said with "you can change the setting that forbids accessing internal API"? I do not, so wonderingOverset
@Overset I wasn't certain but I thought he meant add the --add-opens.Gabrielegabriell
that does not look like a "forbid" though. may be he is talking about some certain internal cglib flag?Overset
Rafael surely meant --illegal-access=…. Prior to JDK 16, its default was --illegal-access=permit, now it’s --illegal-access=deny. You may override it and gain some time until JEP 403 gets implemented and the whole thing will break again.Affiliate
@Affiliate ah! Excellent point, easier then I expectedOverset
@Affiliate --illegal-access=permit resolves the issue as you say. Happy to accept your answer would you care to write one.Gabrielegabriell
The long-run solution is to get an update of cglib that doesn't use internal API points in this way, since relatively soon, the workarounds will stop working. You need to report this problem to the cglib maintainers; this is their problem to fix.Europium
A
31

Since JDK 16, the default for the --illegal-access option is deny, so “deep reflection” to JDK classes fails.

You can override the behavior by specifying --illegal-access=permit, but you have to be aware of JEP 403: Strongly Encapsulate JDK Internals which is about closing that possibility in a future version, so this option is only a temporary solution.

The permanent solution is to update cglib to a compatible version if/once it exists. The attempt to access ClassLoader.defineClass suggests that the library wants to add classes to a particular context, which can be done via MethodHandles.lookup().defineClass instead (since Java 9). So the code only has to switch to the new way of adding classes.

Affiliate answered 8/4, 2021 at 15:2 Comment(7)
fyi, took the liberty to reference this back to the GitHub issue linked in the question for future audienceMyer
From JDK17 on, setting --illegal-access=permit or warn doesn't work any longer. it is still possible to build a workaround by manually specifying "add-opens" for accessed packages in the manifest or as jvmArg (e.g. --add-opens=java.base/java.util=ALL-UNNAMED)Pubilis
@FlorianKoch well, yes, ⅔ of this answer are about the expectation of this to happen. The real fix is described as well. Using --add-opens can also only be a temporary work-around and requires the user to know precisely a) the module/package to open and b) to which target module it needs to be opened.Affiliate
totally true, however in my case I'm using a dependency (that I can't get rid of or update) which is causing the problems, so there is no way to fix the root of the problemPubilis
@FlorianKoch then, there’s no way to fix the problem on your side. But there should be maintainers already working on the problem at the other side. If not, it’s time to think about long term migration…Affiliate
Hello, I've got a similar problem but with java 1.8, and mock ejb, could you I be running with the same issue? Here's a link to my question: #74466339Signorina
@Signorina letting aside that this can’t be java 1.8, it’s precisely the same issue. Maybe, you mean java 18?Affiliate

© 2022 - 2024 — McMap. All rights reserved.