1. Introduction
FreeMarker is a template engine, written in Java, and maintained by the Apache Foundation. We can use the FreeMarker Template Language, also known as FTL, to generate many text-based formats like web pages, email, or XML files.
In this tutorial, we’ll see what we can do out-of-the-box with FreeMarker, though note that it is quite configurable and even integrates nicely with Spring.
Let’s get started!
2. Quick Overview
To inject dynamic content in our pages, we need to use a syntax that FreeMarker understands:
- ${…} in the template will be replaced in the generated output with the actual value of the expression inside the curly brackets – we call this interpolation – a couple of examples are ${1 + 2} and ${variableName}
- FTL tags are like HTML tags (but contain # or @) and FreeMarker interprets them, for example <#if…></#if>
- Comments in FreeMarker start with <#– and end with –>
3. The Include Tag
The FTL include directive is a way for us to follow the DRY principle in our application. We will define the repetitive content in a file and reuse it across different FreeMarker templates with single include tag.
One such use case is when we want to include the menu section inside many pages. First, we’ll define the menu section inside a file – we’ll call it menu.ftl – with the following content:
<a href="#dashboard">Dashboard</a> <a href="#newEndpoint">Add new endpoint</a>
And on our HTML page, let’s include the created menu.ftl:
<!DOCTYPE html> <html> <body> <#include 'fragments/menu.ftl'> <h6>Dashboard page</h6> </body> </html>
And we can also include FTL in our fragments, which is great.
4. Handling Value Existence
FTL will consider any null value as a missing value. Thus, we need to be extra careful and add logic to handle null inside our template.
We can use the ?? operator to check if an attribute, or nested property, exists. The result is a boolean:
${attribute??}
So, we’ve tested the attribute for null, but that’s not always enough. Let’s now define a default value as a fallback for this missing value. To do this, we need the ! operator placed after the name of the variable:
${attribute!'default value'}
Using round brackets, we can wrap many nested attributes.
For example, to check if the attribute exists and has a nested property with another nested property, we wrap everything:
${(attribute.nestedProperty.nestedProperty)??}
Finally, putting everything together, we can embed these among static content:
<p>Testing is student property exists: ${student???c}</p> <p>Using default value for missing student: ${student!'John Doe'}</p> <p>Wrapping student nested properties: ${(student.address.street)???c}</p>
And, if the student were null, we’d see:
<p>Testing is student property exists: false</p> <p>Using default value for missing student: John Doe</p> <p>Wrapping student nested properties: false</p>
Please notice the additional ?c directive used after the ??. We did it to convert the boolean value to a human-readable string.
5. The If-Else Tag
Control structures are present in FreeMarker, and the traditional if-else is probably familiar:
<#if condition> <!-- block to execute if condition is true --> <#elseif condition2> <!-- block to execute if condition2 is the first true condition --> <#elseif condition3> <!-- block to execute if condition3 is the first true condition --> <#else> <!-- block to execute if no condition is true --> </#if>
While the elseif and else branches are optional, the conditions must resolve to a boolean value.
To help us with our evaluations, we’ll likely use one of:
- x == y to check is x is equal to y
- x != y to return true only if x differs from y
- x lt y means that x must be strictly smaller than y – we can also use < instead of lt
- x gt y evaluates to true only if x is strictly greater than y – we can use > instead of gt
- x lte y tests if x is less than or equal to y – the alternative to lte is <=
- x gte y tests if x is greater than or equal to y – the alternative of gte is >=
- x?? to check the existence of x
- sequence?seqContains(x) validates the existence of x inside a sequence
It’s very important to keep in mind that FreeMarker considers >= and > as closing characters for an FTL tag. The solution is to wrap their usage in parentheses or use gte or gt instead.
Putting it together, for the following template:
<#if status??> <p>${status.reason}</p> <#else> <p>Missing status!</p> </#if>
We end up with the resulting HTML code:
<!-- When status attribute exists --> <p>404 Not Found</p> <!-- When status attribute is missing --> <p>Missing status!</p>
6. Containers of Sub-Variables
In FreeMarker, we have three types of containers for sub-variables:
- Hashes are a sequence of key-value pairs – the key must be unique inside the hash and we don’t have an ordering
- Sequences are lists where we have an index associated with each value – a noteworthy fact is that sub-variables can be of different types
- Collections are a special case of sequences where we can’t access the size or retrieve values by index – we can still iterate them with the list tag though!
6.1. Iterating Items
We can iterate over a container in two basic ways. The first one is where we iterate over each value and have logic happening for each of them:
<#list sequence as item> <!-- do something with ${item} --> </#list>
Or, when we want to iterate a Hash, accessing both the key and the value:
<#list hash as key, value> <!-- do something with ${key} and ${value} --> </#list>
The second form is more powerful because it also allows us to define the logic that should happen at various steps in the iteration:
<#list sequence> <!-- one-time logic if the sequence is not empty --> <#items as item> <!-- logic repeated for every item in sequence --> </#items> <!-- one-time logic if the sequence is not empty --> <#else> <!-- one-time logic if the sequence is empty --> </#list>
The item represents the name of the looped variable, but we can rename it to what we want. The else branch is optional.
For a hands-on example, well define a template where we list some statuses:
<#list statuses> <ul> <#items as status> <li>${status}</li> </#items> </ul> <#else> <p>No statuses available</p> </#list>
This will return us the following HTML when our container is [“200 OK”, “404 Not Found”, “500 Internal Server Error”]:
<ul> <li>200 OK</li> <li>404 Not Found</li> <li>500 Internal Server Error</li> </ul>
6.2. Items Handling
A hash allows us two simple functions: keys to retrieve only the keys contained, and values to retrieve only the values.
A sequence is more complex; we can group the most useful functions:
- chunk and join to get a sub-sequence or combine two sequences
- reverse, sort, and sortBy for modifying the order of elements
- first and last will retrieve the first or last element, respectively
- size represents the number of elements in the sequence
- seqContains, seqIndexOf, or seqLastIndexOf to look for an element
7. Type Handling
FreeMarker comes with a huge variety of functions (built-ins) available for working with objects. Let’s see some frequently used functions.
7.1. String Handling
- url and urlPath will URL-escape the string, with the exception that urlPath will not escape slash /
- jString, jsString, and jsonString will apply the escaping rules for Java, Javascript and JSON, respectively
- capFirst, uncapFirst, upperCase, lowerCase and capitalize are useful for changing the case of our string, as implied by their names
- boolean, date, time, datetime and number are functions for converting from a string to other types
Let’s now use a few of those functions:
<p>${'http://myurl.com/?search=Hello World'?urlPath}</p> <p>${'Using " in text'?jsString}</p> <p>${'my value?upperCase}</p> <p>${'2019-01-12'?date('yyyy-MM-dd')}</p>
And the output for the template above will be:
<p>http%3A//myurl.com/%3Fsearch%3DHello%20World</p> <p>MY VALUE</p> <p>Using \" in text</p> <p>12.01.2019</p>
When using the date function, we’ve also passed the pattern to use for parsing the String object. FreeMarker uses the local format unless specified otherwise, for example in the string function available for date objects.
7.2. Number Handling
- round, floor and ceiling can help with rounding numbers
- abs will return a number’s absolute value
- string will convert the number to a string. We can also pass four pre-defined number formats: computer, currency, number, or percent or define our own format, like [ “0.###” ]
Let’s do a chain of a few mathematical operations:
<p>${(7.3?round + 3.4?ceiling + 0.1234)?string('0.##')}</p> <!-- (7 + 4 + 0.1234) with 2 decimals -->
And as expected, the resulting value is 11.12.
7.3. Date Handling
- .now represents the current date-time
- date, time and datetime can return the date and time sections of the date-time object
- string will convert date-times to strings – we can also pass the desired format or use a pre-defined one
We’re going to now get the current time and format the output to a string containing only the hours and minutes:
<p>${.now?time?string('HH:mm')}</p>
The resulting HTML will be:
<p>15:39</p>
8. Exception Handling
We’ll see two ways to handle exceptions for a FreeMarker template.
The first way is to use attempt-recover tags to define what we should try to execute and a block of code that should execute in case of error.
The syntax is:
<#attempt> <!-- block to try --> <#recover> <!-- block to execute in case of exception --> </#attempt>
Both attempt and recover tags are mandatory. In case of an error, it rolls back the attempted block and will execute only the code in the recover section.
Keeping this syntax in mind, let’s define our template as:
<p>Preparing to evaluate</p> <#attempt> <p>Attribute is ${attributeWithPossibleValue??}</p> <#recover> <p>Attribute is missing</p> </#attempt> <p>Done with the evaluation</p>
When attributeWithPossibleValue is missing, we’ll see:
<p>Preparing to evaluate</p> <p>Attribute is missing</p> <p>Done with the evaluation</p>
And the output when attributeWithPossibleValue exists is:
<p>Preparing to evaluate</p> <p>Attribute is 200 OK</p> <p>Done with the evaluation</p>
The second way is to configure FreeMarker what should happen in case of exceptions.
With Spring Boot, we easily configure this via properties file; here are some available configurations:
- spring.freemarker.setting.template_exception_handler=rethrow re-throws the exception
- spring.freemarker.setting.template_exception_handler=debug outputs the stack trace information to the client and then re-throws the exception.
- spring.freemarker.setting.template_exception_handler=html_debug outputs the stack trace information to the client, formatting it so it will be usually well readable in the browser, and then re-throws the exception.
- spring.freemarker.setting.template_exception_handler=ignore skips the failing instructions, letting the template continue executing.
- spring.freemarker.setting.template_exception_handler=default
9. Calling Methods
Sometimes we want to call Java methods from our FreeMarker templates. We’ll now see how to do it.
9.1. Static Members
To start accessing static members, we could either update our global FreeMarker configuration or add a StaticModels type attribute on the model, under the attribute name statics:
model.addAttribute("statics", new DefaultObjectWrapperBuilder(new Version("2.3.28")) .build().getStaticModels());
Accessing static elements is straight-forward.
First, we import the static elements of our class using the assign tag, then decide on a name and, finally, the Java classpath.
Here’s how we’ll import Math class in our template, show the value of the static PI field, and use the static pow method:
<#assign MathUtils=statics['java.lang.Math']> <p>PI value: ${MathUtils.PI}</p> <p>2*10 is: ${MathUtils.pow(2, 10)}</p>
The resulting HTML is:
<p>PI value: 3.142</p> <p>2*10 is: 1,024</p>
9.2. Bean Members
Bean members are very easy to access: use the dot (.) and that’s it!
For our next example, we will add a Random object to our model:
model.addAttribute("random", new Random());
In our FreeMarker template, let’s generate a random number:
<p>Random value: ${random.nextInt()}</p>
This will cause output similar to:
<p>Random value: 1,329,970,768</p>
9.3. Custom Methods
The first step for adding a custom method is to have a class that implements FreeMarker’s TemplateMethodModelEx interface and defines our logic inside the exec method:
public class LastCharMethod implements TemplateMethodModelEx { public Object exec(List arguments) throws TemplateModelException { if (arguments.size() != 1 || StringUtils.isEmpty(arguments.get(0))) throw new TemplateModelException("Wrong arguments!"); String argument = arguments.get(0).toString(); return argument.charAt(argument.length() - 1); } }
We’ll add an instance of our new class as an attribute on the model:
model.addAttribute("lastChar", new LastCharMethod());
The next step is to use our new method inside our template:
<p>Last char example: ${lastChar('mystring')}</p>
Finally, the resulting output is:
<p>Last char example: g</p>
10. Conclusion
In this article, we’ve seen how to use the FreeMarker template engine inside our project. We’ve focused on common operations, how to manipulate different objects, and a few more advanced topics.
The implementation of all these snippets is available over on GitHub.