micronaut-views icon indicating copy to clipboard operation
micronaut-views copied to clipboard

Soy templates in native Graal images

Open sgammon opened this issue 5 years ago • 4 comments

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.

sgammon avatar Feb 13 '20 21:02 sgammon

@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.

sgammon avatar Feb 13 '20 21:02 sgammon

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
  }
]

sgammon avatar Feb 13 '20 22:02 sgammon

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 avatar Feb 14 '20 07:02 graemerocher

@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 :)

sgammon avatar Feb 14 '20 13:02 sgammon