GraalVM native support - Apache FreeMarker Manual

Unsupported web browser - Use a modern browser to view this website!

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# GraalVM native support

Apache FreeMarker is compatible with Ahead-of-Time (AOT) compilation using GraalVM as of version 2.3.35, but with two big caveats:

- Custom Java classes that are exposed to the templates via the data model must be explicitly listed in the GraalVM native reflection configuration, or else the FreeMarker templates will not see their members. That's because the templates are not compiled, and has to be able to discover the exposed classes on runtime via Java reflection.

- If templates are stored as Java resources, the resources also must be explicitly configured to be accessible on runtime, or else FreeMarker will not find the templates.

Let's say you have this class:

    package com.example;

    public class User {
        private String name;
        private String email;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }
    }

Then you put an instance of that into the data model with name `user`, and access it from a template like this, which you put into `src/main/resources/templates`:

Template

    Hello ${user.name}!

    Your e-mail address is: ${user.email}

Then you configure FreeMarker to load the templates from the class path:

    Configuration cfg = new Configuration(Configuration.VERSION_...);
    ...
    cfg.setClassForTemplateLoading(this.getClass(), "/templates");

Under normal Java, all this would just work. But under GraalVM native, if it finds the template at all, the template processing will fail with `freemarker.core.InvalidReferenceException` saying that "user.name has evaluated to null or missing". That's because FreeMarker tries to discover the methods of the `User` class via Java reflection, but such information is by default not available in GraalVM native. You need to add a reflection configuration file like this, which we could put for example into `src/main/graalvm-native-config/reflection-config.json`:

    [
      {
        "name": "com.example.User",
        "allPublicMethods": true
      }
    ]

Then this has to be referred in the GraalVM native build. For example in Gradle it would be something like this:

    graalvmNative {
        binaries.all {
            ...
            configurationFileDirectories.from(file("src/main/graalvm-native-config"))
            resources.autodetect() // Needed to find the templates
            ...
        }
    }

If you are running `native-image` form command line instead, then the corresponding command-line options are (here we assume that template are in `src/main/resources/templates`):

- `-H:ReflectionConfigurationFiles=./src/main/graalvm-native-config/reflection-config.json`

- `-H:IncludeResources=^templates/.*`

After these, the templates should now work.

Note:

This example was written for GraalVM 21. GraalVM and its Gradle plugin is still evolving, so above can become outdated, or not the best practice anymore. Feel free to report any such issue!

You can also find a working example in the FreeMarker source code; see the `freemarker-test-graalvm-native` subproject there!

Please see the GraalVM documentation for more details!

Previous Next