Fix WARNING: Illegal reflective access by javax.el.BeanELResolver with JDK 11
When comiling japkit examples wit JDK 11 and javac:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by javax.el.BeanELResolver (file:/C:/java/maven/repository/org/apache/tomcat/embed/tomcat-embed-el/8.5.8/tomcat-embed-el-8.5.8.jar) to method com.sun.tools.javac.code.Symbol$TypeSymbol.asType()
WARNING: Please consider reporting this to the maintainers of javax.el.BeanELResolver
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
To prepare for future JDKs, this warning should be fixed.
And when using Groovy: WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass$3$1 (file:/C:/java/maven/repository/org/codehaus/groovy/groovy-all/2.4.7/groovy-all-2.4.7-indy.jar) to method java.lang.Object.finalize() WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass$3$1
And when using Groovy: WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass$3$1 (file:/C:/java/maven/repository/org/codehaus/groovy/groovy-all/2.4.7/groovy-all-2.4.7-indy.jar) to method java.lang.Object.finalize() WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass$3$1
this issue is fixed in groovy-all 3.0.0-beta-1, newest version is 3.0.0-beta-2 https://issues.apache.org/jira/browse/GROOVY-8339
When passing --illegal-access=debug (or warn) as VM parameter to the build of japkit-examples, further illegal access messages are revealed from / to;
- javax.el.BeanELResolver to com.sun.tools.javac.code, com.sun.tools.javac.util
- org.codehaus.groovy.reflection.CachedClass to java.lang, java.util, com.sun.tools.javac.code, com.sun.tools.javac.util
- de.japkit.services.ElementsExtensions to com.sun.tools.javac.code (propbably in getValueWithErrorHandling)
Example Stacktrace (with--illegal-access=deny ):
[ERROR] java.lang.IllegalAccessException: class javax.el.BeanELResolver cannot access class com.sun.tools.javac.util.List (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.util to unnamed module @41f686af, cause: class javax.el.BeanELResolver cannot access class com.sun.tools.javac.util.List (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.util to unnamed module @41f686af
java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
java.base/java.lang.reflect.Method.invoke(Method.java:558)
javax.el.BeanELResolver.invoke(BeanELResolver.java:158)
javax.el.CompositeELResolver.invoke(CompositeELResolver.java:79)
javax.el.CompositeELResolver.invoke(CompositeELResolver.java:79)
de.japkit.el.javael3.JapkitELResolver.invoke(JapkitELResolver.java:145)
org.apache.el.parser.AstValue.getValue(AstValue.java:159)
org.apache.el.parser.AstDeferredExpression.getValue(AstDeferredExpression.java:43)
org.apache.el.parser.AstCompositeExpression.getValue(AstCompositeExpression.java:49)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190)
de.japkit.el.javael3.JavaEL3Provider.eval(JavaEL3Provider.java:36)
de.japkit.el.ELSupport.eval(ELSupport.java:329)
- With jdt, there are no warnings related to accessing the compiler API.
- Most of the warnings are groovy related then
For juel (java el 2), there are warnings with both compilers, for example:
Illegal reflective access by javax.el.BeanELResolver (file:/C:/java/maven/repository/de/odysseus/juel/juel-api/2.2.6/juel-api-2.2.6.jar) to method java.util.Collections$UnmodifiableList.get(int)
Adding the following JVM params for the Maven build makes the javac related warnings disappear:
--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
Still no sure, who has to fix something in the long term. Japkit? maven-compiler-plugin? tomcat-embed-el? javac? (since it is working with jdt, it is more likely javac ...)
The solution above is from https://stackoverflow.com/questions/46773519/accessing-com-sun-tools-javac-util-from-java-9. There it is also suggested:
In the longer run, the safe way to be dealing with such situation is to move away from using these internal APIs of the JDK. One can make use of the jdk.compiler module's APIs as a replacement to the com.sun.tools.javac package.
However, japkit does not use com.sun.tools.javac directly, but just the annotation processing API...
Previously, the --add-exports had been added as JVM params to the JVM that runs the Maven Build. That works, since this is the "runtime" for the japkit annotation processor. It does, however, not work to pass --add-exports to the compiler plugin in, since this is obviously an option for the code to be compiled, but not for running the annoatation processor.
From Maven documentation:
...you can define JVM configuration via ${maven.projectBasedir}/.mvn/jvm.config file which means you can define the options for your build on a per project base
When creating this file with the following content, the javac related warnings disappear:
--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
Still sounds like a workaround...
And Java 8 build does not work anymore then. See: https://blog.codefx.org/tools/maven-on-java-9/#Applying-Java-9-Flags-To-Maven-Process
Here is a similar JDK bug for an annotation processor. https://bugs.openjdk.java.net/browse/JDK-8153060 However, the solution was to remove the reflective access...
https://github.com/javaee/metro-jax-ws/commit/90e1cb788b6866891757259e366f14477220348f#diff-32f859368099474565b05c3c29e230a3
And there is code like this:
Class.forName("com.sun.tools.javac.processing.JavacProcessingEnvironment", false, cl)
So, it is quite obvious here that the reflective access in "on" com.sun.tools.javac.
Is it possible to "convince" the JVM that we reflect on java.lang.model and not on com.sun.tools.javac... ? Does it make any difference?
I submitted a bug / enhancement for tomcat-embed-el to look for the interfaces if a type declaring a method is not exported by its module:
https://bz.apache.org/bugzilla/show_bug.cgi?id=63781
Regarding:
Illegal reflective access by de.japkit.services.ElementsExtensions (file:/C:/java/maven/repository/com/github/japkit/japkit-annotation-processor/1.20-SNAPSHOT/japkit-annotation-processor-1.20-SNAPSHOT.jar) to field com.sun.tools.javac.code.Attribute$UnresolvedClass.classType
This is caused by:
if(av?.class.canonicalName.equals("com.sun.tools.javac.code.Attribute.UnresolvedClass")){
//Javac >= 8
val errorType = try {
(av.class.getField("classType").get(av) as TypeMirror)
} catch (Exception e){
...
}
return errorType
}
This could probably be avoided by examining the AST of the AnnotationValue instead by com.sun.source.util.Trees.getTree(Element e, AnnotationMirror a, AnnotationValue v)
However, this is not available before JDK 9. So it must be deciced whether to increase JDK level of japkit before fixing this bug or implementing some JDK8-backward-compatible approach, which only uses AST, if com.sun.source.util.Trees is available.
Besides, the Trees-API could also help with ErrorTypes, especially with TypesRegistry guessTypeNameFromToString() and also with getting a FQN for the error type by scanning the import statements of the CompilationUnit.
The AST of Annotation Value of type Class<?> is a MemberSelectTree where the identifier is "class" and the expression is the name of the class (potentially another MemberSelectTree if the class name is partially or fully qualified).
Trees.getTree does also work if the AnnotationValue is within an array.
I have not seen those warnigns anymore wiht japkit 2.0, based on Java 17, Groovy 5, Tomcat EL 10.x But I have to check in more detail to be sure.
Regarding:
Illegal reflective access by de.japkit.services.ElementsExtensions (file:/C:/java/maven/repository/com/github/japkit/japkit-annotation-processor/1.20-SNAPSHOT/japkit-annotation-processor-1.20-SNAPSHOT.jar) to field com.sun.tools.javac.code.Attribute$UnresolvedClass.classTypeThis is caused by:
if(av?.class.canonicalName.equals("com.sun.tools.javac.code.Attribute.UnresolvedClass")){ //Javac >= 8 val errorType = try { (av.class.getField("classType").get(av) as TypeMirror) } catch (Exception e){ ... } return errorType }This could probably be avoided by examining the AST of the AnnotationValue instead by
com.sun.source.util.Trees.getTree(Element e, AnnotationMirror a, AnnotationValue v)However, this is not available before JDK 9. So it must be deciced whether to increase JDK level of japkit before fixing this bug or implementing some JDK8-backward-compatible approach, which only uses AST, if com.sun.source.util.Trees is available.
Besides, the Trees-API could also help with ErrorTypes, especially with TypesRegistry guessTypeNameFromToString() and also with getting a FQN for the error type by scanning the import statements of the CompilationUnit.
Okay, debugging the build of japkit examples shows that this issue is still there:
java.lang.IllegalAccessException: class de.japkit.services.ElementsExtensions cannot access class com.sun.tools.javac.code.Attribute$UnresolvedClass (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.code to unnamed module @2f95ce11
It seems, that this does not finally lead to an error. It think that is primarily due to the fact that there are no cyclic dependencies between the genreated classes in the examples.