micronaut-projects / micronaut-views

Micronaut Integration with Server Side View Rendering
Apache License 2.0
29 stars 32 forks source link

Soy templates in native Graal images #32

Open sgammon opened 4 years ago

sgammon commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

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
  }
]
graemerocher commented 4 years ago

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

sgammon commented 4 years ago

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