Groovy: 2.5.15
Java: 11
I have a java class:
#Data
public class SpecialDTO {
Long originId;
}
But, when I access it from my Groovy code:
Long myId = specialdto.originId
I get this exception:
java.lang.ClassCastException: class SpecialDTO cannot be cast to class groovy.lang.GroovyObject (SpecialDTO and groovy.lang.GroovyObject are in unnamed module of loader 'app')
Which appears bogus as I can insert this in the code and it works:
GroovyObject obj = specialdto
We do this all over the place, but this specific test is failing. I can substitute 'getOriginId()' and get the desired results, but it's just such an odd error given that it only happens with this object.
Related
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 com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:77)
at javax.faces.api#3.1.0.SP01//javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
at javax.faces.api#3.1.0.SP01//javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:181)
at javax.faces.api#3.1.0.SP01//javax.faces.component.UIOutput.getValue(UIOutput.java:140)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:198)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:328)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:143)
at javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:600)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:286)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:90)
at javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:571)
at javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1648)
at javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1651)
at javax.faces.api#3.1.0.SP01//javax.faces.component.UIComponent.encodeAll(UIComponent.java:1651)
at com.sun.jsf-impl#2.3.17.SP01//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 javax.el.api#2.0.0.Final//javax.el.BeanELResolver.getValue(BeanELResolver.java:193)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:156)
at com.sun.jsf-impl#2.3.17.SP01//com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:184)
at org.glassfish.jakarta.el#3.0.3.jbossorg-4//com.sun.el.parser.AstValue.getValue(AstValue.java:114)
at org.glassfish.jakarta.el#3.0.3.jbossorg-4//com.sun.el.parser.AstValue.getValue(AstValue.java:177)
at org.glassfish.jakarta.el#3.0.3.jbossorg-4//com.sun.el.parser.AstDeferredExpression.getValue(AstDeferredExpression.java:39)
at org.glassfish.jakarta.el#3.0.3.jbossorg-4//com.sun.el.parser.AstCompositeExpression.getValue(AstCompositeExpression.java:44)
at org.glassfish.jakarta.el#3.0.3.jbossorg-4//com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:183)
at org.jboss.weld.core#3.1.9.Final//org.jboss.weld.module.web.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at org.jboss.weld.core#3.1.9.Final//org.jboss.weld.module.web.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at com.sun.jsf-impl#2.3.17.SP01//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 javax.el.api#2.0.0.Final//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}
When switching from Groovy 2 to Groovy 3, ModelMapper 2.4.4 seems to now be failing to convert objects. ModelMapper itself does not throw an error, but rather just returns an object whose metaClass is still the initial class rather than the new post-conversion class.
This is demonstrated in the below code which, when run with Groovy 3 (tested with 3.0.2 and 3.0.9), then throws java.lang.IllegalArgumentException: object is not an instance of declaring class when accessing any of the properties of the returned object post-ModelMapping. This error does not happen when run in Groovy 2 (2.5.15).
Dependencies:
org.modelmapper:modelmapper:2.4.4
org.codehaus.groovy:groovy:3.0.9
import org.modelmapper.ModelMapper
class TestClass {
String fieldA
String fieldB
}
class TestClassDto {
String fieldA
String fieldB
}
TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')
System.out.println(new ModelMapper().map(test, TestClass).fieldA)
The issue is in the fact that metaClass gets automatically mapped (so, testclass.metaClass gets replaces with TestClass.metaClass and groovy considers the final object to be an instance of TestClass). There are multiple ways to fix this.
You can explicitly set the metaclass after the mapping has been done:
def 'testMapping'() {
given:
TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')
def mapped = new ModelMapper().map(test, TestClass)
mapped.metaClass = TestClass.metaClass
expect:
mapped.fieldA == 'anyA'
}
Alternatively, use #CompileStatic so that metaclass isn't generated at all.
Or you can even configure Modelmapper to skip metaclass:
mapper.typeMap(TestClassDto, TestClass)
.addMappings({ it.skip(GroovyObject::setMetaClass) } as ExpressionMap)
I'm having trouble with (or I'm confused about) using #Immutability.
Given this class:
#Immutable
class TestField {
String key
}
I would expect I could not set a key on an instance like:
class TestFieldSpec extends Specification {
def 'do stuff'() {
when:
def t = new TestField('a')
println t
t.key = 'b'
println t
then:
t
}
}
However, when I run the test, I do not see a ReadOnlyPropertyException:
Running TestFieldSpec
TestField(a)
TestField(b)
This has been the case for me using both the groovy-eclipse-compiler and gmaven-plus with Maven.
Groovy version 2.4.7.
If I run javap on the class file created by both, I see:
public final void setKey(java.lang.String);
When I do the same in GroovyConsole though, things work how I'd expect them to.
#groovy.transform.Immutable
class TestField {
String key
}
def f = new TestField('a')
f.key = 'b'
Produces:
Exception thrown
groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: key for class: TestField
at TestField.setProperty(ConsoleScript1)
at ConsoleScript1.run(ConsoleScript1:7)
In the Groovy AST Browser, at Phase Finalization, I do not see the setter.
Any insight would be greatly appreciated.
Thanks!
I have the following Groovy file "test.groovy":
import groovy.transform.CompileStatic
#CompileStatic
class Test {
final Set<String> HISTORY = [] as HashSet
Set<String> getHistory() {
return HISTORY.clone() as HashSet<String>
}
}
Test test = new Test()
println test.history
Compiling it with Groovy 2.4.1 works fine, however, when I run "groovy test.class" I get the following error:
Caught: java.lang.VerifyError:
(class: Test, method: getHistory signature:()Ljava/util/Set;)
Bad access to protected data
java.lang.VerifyError:
(class: Test, method: getHistory
signature: ()Ljava/util/Set;)
Bad access to protected data
at test.run(test.groovy:12)
Any ideas what I am doing wrong here?
This actually is a bug in Groovy. A ticket was filed: https://issues.apache.org/jira/browse/GROOVY-7325
Workaround in this case:
It works if you are using a final HashSet<String> and then cast the clone up. Since the getter overrides the property basically anyway (make it private if you wanna be sure), it should not harm the intention of the original code.
I'm afraid I'm a beginner in Groovy so this might be very silly. (using groovy 2.3.4)
I have the following code:
class Test {
public void method() {
def methodName = "dynamicMethodName"
this.metaClass."$methodName" = {->}
this."${methodName}"()
}
}
new Test().method()
Running this will throw the following error:
Caught: groovy.lang.MissingPropertyException: No such property: dynamicMethodName for class: groovy.lang.MetaClassImpl
groovy.lang.MissingPropertyException: No such property: dynamicMethodName for class: groovy.lang.MetaClassImpl
at Test.method(what.groovy:5)
at Test$method.call(Unknown Source)
at what.run(what.groovy:10)
Can someone help me figure out what I am doing wrong? I also tried not being 'too dynamic' and altered the code like so:
class Test {
public void method() {
this.metaClass.dynamicMethodName = {->}
this.dynamicMethodName()
}
}
new Test().method()
But I still get the same error
UPDATE
It seems that if I add the method outside the class by using an instance reference instead of using this, it works.
Is there no way to dynamically add methods for an instance from within the instance itself?
It turns out, if the class extends the class Expando, it works.
Answer found here: https://stackoverflow.com/a/7079038/56242