PebbleTemplates / pebble

Java Template Engine
https://pebbletemplates.io
BSD 3-Clause "New" or "Revised" License
1.09k stars 166 forks source link

Add native hints #648

Closed noblehelm closed 1 year ago

noblehelm commented 1 year ago

In order to let any Java project including PebbleTemplates as a dependency to be able to build and run a native image using the AOT-compiler GraalVM, it is necessary to add native hints to the GraalVM so that it can maintain class and variables information during compile time (and consequently runtime) to be used as information for Reflection, Proxies and other JVM mechanisms.

This will add the necessary TypeHints for the SpringBoot starter only, in order to enable the building of a project using Spring Boot as framework, and to promote further experiments with other frameworks.

noblehelm commented 1 year ago

Proposed fix for #647

noblehelm commented 1 year ago

Unfortunately, even with the added hints for the ForNode class, for the inner LoopVariable class, it is not possible to let the native image reach its fields due to the fact that it is a private class. So, even with the hints, we end up with a similar error to this one:

2023-03-18 00:29:24.390 -ERROR 1 --- [nio-7300-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/api/planrepository] threw exception [Request processing failed: io.pebbletemplates.pebble.error.PebbleException: Could not perform addition (**template**:6)] with root cause

io.pebbletemplates.pebble.error.AttributeNotFoundException: Attribute [index] of [io.pebbletemplates.pebble.node.ForNode$LoopVariables] does not exist or can not be accessed and strict variables is set to true. (**template**:6)
    at io.pebbletemplates.pebble.node.expression.GetAttributeExpression.evaluate(GetAttributeExpression.java:91) ~[native:na]
    at io.pebbletemplates.pebble.node.expression.AddExpression.evaluate(AddExpression.java:22) ~[native:na]
    at io.pebbletemplates.pebble.node.expression.FilterExpression.evaluate(FilterExpression.java:66) ~[native:na]
    at io.pebbletemplates.pebble.node.PrintNode.render(PrintNode.java:37) ~[na:na]
    at io.pebbletemplates.pebble.node.BodyNode.render(BodyNode.java:44) ~[native:na]
    at io.pebbletemplates.pebble.node.ForNode.render(ForNode.java:125) ~[native:na]
    at io.pebbletemplates.pebble.node.BodyNode.render(BodyNode.java:44) ~[native:na]
    at io.pebbletemplates.pebble.node.RootNode.render(RootNode.java:31) ~[na:na]
    at io.pebbletemplates.pebble.template.PebbleTemplateImpl.evaluate(PebbleTemplateImpl.java:157) ~[native:na]
    at io.pebbletemplates.pebble.template.PebbleTemplateImpl.evaluate(PebbleTemplateImpl.java:96) ~[native:na]

Is there any way to solve this?

ebussieres commented 1 year ago

Never had the chance to try GraalVM. Can't help you on that one

fabriceci commented 1 year ago

I haven't tested it but I wanted to one day now that it works out of the box on Spring boot. @noblehelm Were you able to work around the problem?

If you override the class by creating in your project ,the same class file in the same package (ugly but works), and make it public, does that solve the problem?

noblehelm commented 1 year ago

I've managed to make it work by transforming the LoopVariable class into an immutable class/object, with private fields and its respective Getters, and making it public instead of private. Then, it was just a jump over into adding the apropriate type hint for the LoopVariable and everything worked.

I've pushed a new commit to the PR, will do a squash if required. I'll try to give some time this week to test all of the different expressions to see if these hints already do cover what is needed (because I don't know if my project does use all the different expressions).

ebussieres commented 1 year ago

Ok let me know when you are done testing

noblehelm commented 1 year ago

I've finished the tests and included the resource file native-image.peb I have crafted with all of the examples from each different functionality listed on the documentation page (except for the inherit functionality, which required three diferentes pebs. I've done the tests with those and it worked, but I decided to not include here to not pollute the resource tests). I included the resource file, even though there is no unit test that actually uses it, for "historical" purposes.

Upon tests, the only expression that actually required the type hint was the UnaryMinus. All of the rest worked out-of-the-box with the Spring integration. So, I removed the code in order to reflect only what is actually needed. If sometime in the future another expression is to be added, maybe a type hint can be added there IF it does require the hint. Otherwise, there is no necessity to do it.

noblehelm commented 1 year ago

If you wish, I can squash the commits before merge.

noblehelm commented 1 year ago

Any updates?

ebussieres commented 1 year ago

Thanks ! Will try to do a release soon !