While porting a big JEE8 application to Java 17, I stumbled upon an IllegalAccessException when rendering a simple EL expression: #{myWarBean.defaultTZ.rawOffset}. I managed to reproduce the problem in a SSCCE on github. When you run the application on Wildfly application server (I'm using 26.1.1.Final), you get the following stacktrace:
SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (default task-1) Error Rendering View[/index.xhtml]: javax.el.ELException: /index.xhtml @23,74 value="raw offset=#{myWarBean.defaultTZ.rawOffset}": java.lang.IllegalAccessException: class javax.el.BeanELResolver cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @6a1cb0de
at [email protected]//com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:77)
at [email protected]//javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
at [email protected]//javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:181)
at [email protected]//javax.faces.component.UIOutput.getValue(UIOutput.java:140)
at [email protected]//com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:198)
at [email protected]//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:328)
at [email protected]//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:143)
at [email protected]//javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:600)
at [email protected]//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:286)
at [email protected]//com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:90)
at [email protected]//javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:571)
at [email protected]//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1648)
at [email protected]//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1651)
at [email protected]//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1651)
at [email protected]//com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:461)
[...]
Caused by: javax.el.ELException: java.lang.IllegalAccessException: class javax.el.BeanELResolver cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @6a1cb0de
at [email protected]//javax.el.BeanELResolver.getValue(BeanELResolver.java:193)
at [email protected]//com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:156)
at [email protected]//com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:184)
at [email protected]//com.sun.el.parser.AstValue.getValue(AstValue.java:114)
at [email protected]//com.sun.el.parser.AstValue.getValue(AstValue.java:177)
at [email protected]//com.sun.el.parser.AstDeferredExpression.getValue(AstDeferredExpression.java:39)
at [email protected]//com.sun.el.parser.AstCompositeExpression.getValue(AstCompositeExpression.java:44)
at [email protected]//com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:183)
at [email protected]//org.jboss.weld.module.web.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at [email protected]//org.jboss.weld.module.web.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at [email protected]//com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:73)
... 73 more
Caused by: java.lang.IllegalAccessException: class javax.el.BeanELResolver cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @6a1cb0de
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
at java.base/java.lang.reflect.Method.invoke(Method.java:560)
at [email protected]//javax.el.BeanELResolver.getValue(BeanELResolver.java:186)
... 83 more
It seems, the problem is the EL expression accessing a java.util.TimeZone. The TimeZone class uses sun.util.calendar.ZoneInfo internally. And it seems this is not legal any more.
This only happens with Java 17. When running in Java 11, this all works fine.
I can workaround the exception by adding the following arguments when starting wildfly:
--add-exports=java.base/sun.util.calendar=ALL-UNNAMED
However, I think it should be possible to run the example without this workaround.
Any ideas what I'm missing? Might this even be a bug in Java/JDK 17?
It's reproducible with a plain Java application class as follows:
package com.stackoverflow.q72361100;
import java.lang.reflect.Method;
import java.util.TimeZone;
public class Test {
public static void main(String... args) throws Exception {
TimeZone instance = TimeZone.getDefault();
Class<?> cls = instance.getClass();
Method method = cls.getMethod("getRawOffset");
Object result = method.invoke(instance); // java.lang.IllegalAccessException
System.out.println(result);
}
}
The issue here is that instance.getClass() returns sun.util.calendar.ZoneInfo as that's the implementation returned by TimeZone#getDefault(). The work around would be to use TimeZone.class instead of instance.getClass():
package com.stackoverflow.q72361100;
import java.lang.reflect.Method;
import java.util.TimeZone;
public class Test {
public static void main(String... args) throws Exception {
TimeZone instance = TimeZone.getDefault();
Class<?> cls = TimeZone.class; // Work around
Method method = cls.getMethod("getRawOffset");
Object result = method.invoke(instance);
System.out.println(result);
}
}
I'd argue that this will require a change in EL spec. Ideally it should search further in declared super classes if the method is accessible as per Method#canAccess() and then use it instead.
package com.stackoverflow.q72361100;
import java.lang.reflect.Method;
import java.util.TimeZone;
public class Test {
public static void main(String... args) throws Exception {
TimeZone instance = TimeZone.getDefault();
Class<?> cls = instance.getClass();
Method method = getAccessibleMethod(instance, cls, "getRawOffset"); // Look in superclasses as well.
Object result = method.invoke(instance);
System.out.println(result);
}
private static Method getAccessibleMethod(Object instance, Class<?> cls, String methodName) throws NoSuchMethodException {
Method method = cls.getMethod(methodName);
if (method.canAccess(instance)) {
return method;
}
return getAccessibleMethod(instance, cls.getSuperclass(), methodName);
}
}
I've created an issue at EL spec: https://github.com/jakartaee/expression-language/issues/188
Until they get it fixed, you can work around it by adding a dedicated getter for it:
public int getDefaultTZrawOffset() {
return getDefaultTZ().getRawOffset();
}
#{myWarBean.defaultTZrawOffset}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With