micronaut-views
micronaut-views copied to clipboard
Soy templates in native Graal images
I'm trying to use the Views layer, specifically with Soy, in a native GraalVM image. Although it works fine when executing on the JVM, the native-image
tool seems to cull the compiled template code no matter what I do.
For instance, I've tried to embed JSON reflection configs, to no avail. I've tried statically referencing the template classes in a custom template loader, but it still elides them at runtime and produces a 404 (yielded by the Soy layer, not Micronaut itself).
I'm filing this as a placeholder so I can discuss possible fix approaches with the Micronaut team. Since we are already using the @View
annotation, perhaps there is a way to either (1) generate static references to templates using DI, or (2) generate requisite JSON config entries to prevent templates from being rewritten.
This issue stems naturally from Soy's template loader's use of reflection to load template classes (see the use of Class.forName
here). If this can't be solved within the Micronaut universe, there are alternatives - for instance, a code-generated template loader that relies on a compile-time plugin, rather than reflection.
@graemerocher I know this is at the nexus of two projects you don't control (Soy and Graal), in addition to Micronaut. But, being that you have worked with the tool so much, if you have any guidance about how I might fix this, I would love to push a fix or help work on one. Soy with Micronaut is a powerful layer, but without support for native-image
we lock ourselves out of some awesome functionality.
okay, i've got a working static sample locally. basically, i just need to generate the following JSON using whatever tool micronaut already uses for controllers:
[
{
"name" : "template.class.Here",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"allDeclaredClasses" : true,
"allPublicClasses" : true
},
{
"name" : "template.class.Here$Factory",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"allDeclaredClasses" : true,
"allPublicClasses" : true
}
]
I thought Soy didn't use reflection? If so than only allDeclaredConstructors
would be needed. It may be worth writing compile time integration to generate this configuration. For example https://github.com/micronaut-projects/micronaut-core/blob/1.3.x/graal/src/main/java/io/micronaut/graal/reflect/GraalTypeElementVisitor.java
@graemerocher it only uses reflection once, to load a Factory
embedded class from each template. the Class.forName
itself isn't technically reflection, just dynamic class loading.
if using the old Soy runtime (SoyTofu
), it interprets templates on the fly. apparently with SoySauce
, the new runtime, it loads bytecode-precompiled templates via Class.forName
, translated from a template path.
i'll give it a try with allDeclaredConstructors
, and thank you for the useful link :)