micronaut-views
micronaut-views copied to clipboard
Thymeleaf and Velocity Map access fails in native image
Expected Behavior
No response
Actual Behaviour
Basically the same error as in https://github.com/micronaut-projects/micronaut-views/issues/257
00:01:05.154 [io-executor-thread-1] ERROR org.thymeleaf.TemplateEngine - [THYMELEAF][io-executor-thread-1] Exception processing template "home": Exception evaluating OGNL expression: "security.attributes.get('foo')" (template: "home" - line 7, col 16)
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating OGNL expression: "security.attributes.get('foo')" (template: "home" - line 7, col 16)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:191)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:95)
at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136)
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:661)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1067)
at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.render(ThymeleafViewsRenderer.java:123)
at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.lambda$render$0(ThymeleafViewsRenderer.java:110)
at io.micronaut.core.io.Writable.writeTo(Writable.java:77)
at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$encodeHttpResponse$6(RoutingInBoundHandler.java:946)
at io.micronaut.scheduling.instrument.InvocationInstrumenterWrappedRunnable.run(InvocationInstrumenterWrappedRunnable.java:47)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:597)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:194)
Caused by: ognl.MethodFailedException: Method "get" failed for object {foo=bar}
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1932)
at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68)
at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1996)
at ognl.ASTMethod.getValueBody(ASTMethod.java:91)
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at ognl.SimpleNode.getValue(SimpleNode.java:258)
at ognl.ASTChain.getValueBody(ASTChain.java:141)
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at ognl.SimpleNode.getValue(SimpleNode.java:258)
at ognl.Ognl.getValue(Ognl.java:537)
at ognl.Ognl.getValue(Ognl.java:501)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.executeExpression(OGNLVariableExpressionEvaluator.java:326)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:170)
... 25 common frames omitted
Caused by: java.lang.NoSuchMethodException: java.util.HashMap.get(java.lang.String)
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1873)
... 37 common frames omitted
Exception in thread "io-executor-thread-1" io.micronaut.views.exceptions.ViewRenderingException: Error rendering Thymeleaf view [home]: Exception evaluating OGNL expression: "security.attributes.get('foo')" (template: "home" - line 7, col 16)
at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.render(ThymeleafViewsRenderer.java:125)
at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.lambda$render$0(ThymeleafViewsRenderer.java:110)
at io.micronaut.core.io.Writable.writeTo(Writable.java:77)
at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$encodeHttpResponse$6(RoutingInBoundHandler.java:946)
at io.micronaut.scheduling.instrument.InvocationInstrumenterWrappedRunnable.run(InvocationInstrumenterWrappedRunnable.java:47)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:597)
at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:194)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating OGNL expression: "security.attributes.get('foo')" (template: "home" - line 7, col 16)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:191)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:95)
at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136)
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:661)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1067)
at io.micronaut.views.thymeleaf.ThymeleafViewsRenderer.render(ThymeleafViewsRenderer.java:123)
... 9 more
Caused by: ognl.MethodFailedException: Method "get" failed for object {foo=bar} [java.lang.NoSuchMethodException: java.util.HashMap.get(java.lang.String)]
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1932)
at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68)
at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1996)
at ognl.ASTMethod.getValueBody(ASTMethod.java:91)
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at ognl.SimpleNode.getValue(SimpleNode.java:258)
at ognl.ASTChain.getValueBody(ASTChain.java:141)
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at ognl.SimpleNode.getValue(SimpleNode.java:258)
at ognl.Ognl.getValue(Ognl.java:537)
at ognl.Ognl.getValue(Ognl.java:501)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.executeExpression(OGNLVariableExpressionEvaluator.java:326)
at org.thymeleaf.standard.expression.OGNLVariableExpressionEvaluator.evaluate(OGNLVariableExpressionEvaluator.java:170)
... 25 more
Caused by: java.lang.NoSuchMethodException: java.util.HashMap.get(java.lang.String)
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1873)
... 37 more
Steps To Reproduce
-
Create an app with features
views-thymeleaf
andgraalvm
mn create-app com.example.demo --features=views-thymeleaf,graalvm
-
Create a controller with a method that creates a model similar to what's available when using security, i.e. a
security
map containing anattributes
map:
package com.example;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.views.View;
import java.util.HashMap;
import java.util.Map;
@Controller
class HomeController {
@View("home")
@Get
Map<String, Object> index() {
Map<String, Object> attributes = new HashMap<>();
attributes.put("foo", "bar");
Map<String, Object> security = new HashMap<>();
security.put("attributes", attributes);
Map<String, Object> model = new HashMap<>();
model.put("security", security);
return model;
}
}
- Create
src/main/resources/views/home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<body>
<h2>foo: <span th:text="${security.attributes.get('foo')}"></span></h2>
</body>
</html>
- Start the app and open http://localhost:8080/ in a browser and the output should be
foo: bar
- Build a native image, run the app and open http://localhost:8080/ and it fails
Environment Information
GraalVM version 22.0.0.2.r11-grl
Example Application
No response
Version
3.3.4
I tried switching to Velocity and that failed too, but with different output - instead of a stacktrace, the expression is rendered unprocessed to the HTML, e.g. Username: $security.attributes.get('user_displayname')
You can add:
package com.example;
import io.micronaut.core.annotation.TypeHint;
import java.util.HashMap;
import java.util.Map;
@TypeHint(value = {Map.class, HashMap.class}, accessType = TypeHint.AccessType.ALL_DECLARED_METHODS)
public class ThymleafGraalConfig {
}
which will generate at build/classes/java/main/META-INF/native-image/xxx/xxx/reflect-config.json
:
[ {
"name" : "java.util.Map",
"allDeclaredMethods" : true
}, {
"name" : "java.util.HashMap",
"allDeclaredMethods" : true
} ]
One way to see what you need to generate is to add the following snippet to build.gradle
.
run {
jvmArgs = [
"-agentlib:native-image-agent=experimental-class-loader-support,config-output-dir=tmp",
"-Dorg.graalvm.nativeimage.imagecode=agent"
]
}
run the app with a GraalVM JDK but JIT:
% sdk use java 22.1.0.r11-grl
% ./gradlew run
A folder tmp
is created by the GraalVM agent with information about the necessary reflect-config.json
Does the snippet with which you see what to generate still apply? I'm not aware of the changes since 1,5y in that area.