Freemarker Templating Engine

Freemarker is a very popular and mature templating engine. Its integration as a Quarkus extension provides developers ease of configuration, and offers support for native images. In this guide, you will learn how to easily render Freemarker templates in your application.

Hello World

If you want to use Freemarker you need to add the quarkiverse-freemarker extension first. In your pom.xml file, add:

<dependency>
    <groupId>io.quarkiverse</groupId>
    <artifactId>quarkiverse-freemarker</artifactId>
</dependency>

We’ll start with a very simple template:

hello.ftl
Hello ${name}! (1)
1 ${name} is a value expression that is evaluated when the template is rendered.

Now let’s inject the template in the resource class.

HelloResource.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

import freemarker.template.Template;
import io.quarkiverse.freemarker.runtime.TemplatePath;

@Path("hello")
public class HelloResource {

    @Inject
    @TemplatePath("hello.ftl") (1)
    Template hello;

    @GET
    @Produces(TEXT_PLAIN)
    public String hello(@QueryParam("name") String name) throws IOException, TemplateException {
        StringWriter stringWriter = new StringWriter();
        hello.process(Map.of("name", name), stringWriter); (2)
        return stringWriter.toString();
    }
}
1 The @TemplatePath qualifier specifies the template name, found in either jar resource paths or external file paths.
2 Map.of("name", name) provides the model.

If your application is running, you can request the endpoint:

$ curl -w "\n" http://localhost:8080/hello?name=bob
Hello bob!

Usage

The application code can either directly inject a Freemarker Template bean as we saw above. In that case it must provide a TemplatePath qualifier annotation with the name of the template to select.

Or it can inject a Freemarker Configuration bean, from which templates can be obtained programmatically. The Configuration is a Singleton DefaultBean that can be replaced by an application provided implementation.

The default Configuration bean is configured from quarkus exposed properties. Among those, 2 are build time:

  • resource-paths is a list of resource paths contained in the classpath (e.g. typically in jars of the application). Each path is traversed recursively, and all files get added as native resources when building a native image.

  • directives is a map of directive alias to directive class name. Each directive class is added as a shared variable in the Configuration bean.

Here is an example of using a Configuration bean instead of a Template bean:

    @Inject
    Configuration configuration;

    @GET
    @Produces(TEXT_PLAIN)
    public String hello(@QueryParam("name") String name, @QueryParam("ftl") String ftl) throws IOException, TemplateException {
        StringWriter stringWriter = new StringWriter();
        configuration.getTemplate(ftl).process(model, stringWriter);
        return stringWriter.toString();
    }

The rest of the configuration properties can be provided at runtime, such as a list of external filesystem paths where to find additional templates, and optional properties that can be configured on the Freemarker Configuration class.

Directives

Freemarker can be extended using custom directives. For instance if we wanted to transform some text in base64, we could write:

package org.acme.quarkus.sample;

public class Base64Directive implements TemplateDirectiveModel {
    @Override
    public void execute(Environment environment, Map map, TemplateModel[] templateModels, TemplateDirectiveBody body)
            throws TemplateException, IOException {
        StringWriter sw = new StringWriter();
        body.render(sw);
        byte[] bytes = Base64.getEncoder().encode(sw.toString().getBytes(UTF_8));
        environment.getOut().write(new String(bytes, UTF_8));
    }
}

Then we would have to configure the application with:

quarkus.freemarker.directives.base64=org.acme.quarkus.sample.Base64Directive

And finally, we would use it in templates such as:

Hello <@base64>$\{name}</@base64>!

This would be rendered as:

Hello Ym9i!

Data model

Freemarker supports Map or Object based data models. When using objects, it is important to configure quarkus.freemarker.object-wrapper-expose-fields=true if model classes do not have getters.

Additionnaly, they should be annotated with @RegisterForReflection to support native images.

Freemarker Configuration Reference

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

Comma-separated list of absolute resource paths to scan recursively for templates. All tree folder from 'resource-paths' will be added as a resource. Unprefixed locations or locations starting with classpath will be processed in the same way.

list of string

freemarker/templates

Comma-separated of file system paths where freemarker templates are located

list of string

Set the preferred charset template files are stored in.

string

Sets how errors will appear. rethrow, debug, html-debug, ignore.

string

If false, don’t log exceptions inside FreeMarker that it will be thrown at you anyway.

boolean

Wrap unchecked exceptions thrown during template processing into TemplateException-s.

boolean

If false, do not fall back to higher scopes when reading a null loop variable.

boolean

The string value for the boolean true and false values, usually intended for human consumption (not for a computer language), separated with comma.

string

Sets the default number format used to convert numbers to strings.

string

If true, the object wrapper will be configured to expose fields.

boolean

List of directives to register with format name=classname

Map<String,String>