Java Templating With Rocker
This tutorial will dive into templating with Rocker, a Java 8 template engine designed to be statically typed, and the pros and cons of using this tool.
Join the DZone community and get the full member experience.
Join For FreeIn this post, we will have a quick look at Rocker, a statically typed and fast Java 8 template engine.
Required Dependencies
To get started with Rocker, we need to add the following dependencies to our project:
<dependency>
<groupId>com.fizzed</groupId>
<artifactId>rocker-runtime</artifactId>
<version>0.24.0</version>
</dependency>
<!-- for hot-reloading support in development -->
<dependency>
<groupId>com.fizzed</groupId>
<artifactId>rocker-compiler</artifactId>
<version>0.24.0</version>
</dependency>
We also need the Rocker Maven plugin, which converts Rocker templates to Java code:
<build>
<plugins>
<plugin>
<groupId>com.fizzed</groupId>
<artifactId>rocker-maven-plugin</artifactId>
<version>0.24.0</version>
<executions>
<execution>
<id>generate-rocker-templates</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Creating the First Templates
We will start with a simple example containing two Rocker templates and a Java class.
In the first template (main.rocker.html), we define the basic page structure:
@args (String title, RockerBody content)
<html>
<head>
<title>@title</title>
</head>
<body>
@content
</body>
</html>
This template has two parameters: The page title and a RockerBody element named content. The content parameter is basically a sub-template that is added to the <body> tag.
Now we create another template (basic.rocker.html) that defines the content that is passed to our first template:
@args (String name)
@views.main.template("Basic example") -> {
<h1>Hello @name!</h1>
}
This template has a single name parameter that is written to a <h1> tag. We reference the first template (main.rocker.html) with @views.main and use the template(..) method to render it. "Basic example" will be passed as title parameter. The content parameter is defined within the curly braces.
Rendering Rocker templates
Rocker templates are converted to Java code. This can be done using the following Maven command:
mvn generate-sources
This generates a Java class for each template in the target/generated-sources/rocker directory.
Now we can render our templates using Java code:
public class Main {
public static void main(String... args) throws Exception {
// The template "basic.rocker.html" got converted to the class named "basic"
// which is located in the "views" package
String result = views.basic.template("John")
.render()
.toString();
System.out.println(result);
}
}
The output we get looks like this:
<html>
<head>
<title>Basic example</title>
</head>
<body>
<h1>Hello John!</h1>
</body>
</html>
A bit of a problem with this approach is that you need to run mvn generate-sources everytime you make a change in your template files. Also note, that your Java code might not compile if the code generation fails because you have an issue in one of your templates.
Another possible approach is to use hot reloading and reference the templates dynamically via file names.
In this case, the Java code looks like this:
public class Main {
public static void main(String... args) throws Exception {
// Enable hot reloading
RockerRuntime.getInstance().setReloading(true);
// Reference via string instead of using generated code
String result = Rocker.template("views/basic.rocker.html")
.bind("name", "John")
.render()
.toString();
System.out.println(result)
}
}
The output we get is exactly the same.
Expressions
Assume we have a simple User class that contains the methods getFirstName() and getLastName(). In the following example we see how we can work with the User object in a Rocker template:
@import com.mscharhag.rockerexample.*
@args (User user)
@views.main.template("Expressions") -> {
<ul>
<!-- first name -->
<li>@user.getFirstName()</li>
<!-- first three letters of first name -->
<li>@user.getFirstName().substring(0, 3)</li>
<!-- first name and last name -->
<li>@user.getFirstName() @user.getLastName()</li>
<!-- using a variable-->
@name => {
@user.getFirstName() @user.getLastName()
}
<li>@name</li>
</ul>
}
Conditions
Rocker supports the standard Java if-else flow structure as the following example shows:
@import com.mscharhag.rockerexample.*
@args (User user)
@views.main.template("Conditions") -> {
@if (user.getFirstName().length() > 10) {
<p>Long first name</p>
} else {
<p>short first name</p>
}
}
Loops
Rocker templates support different forms of loops:
@import com.mscharhag.rockerexample.*
@import java.util.*
@args (List<User> users)
@views.main.template("Loops") -> {
<ul>
<!-- standard for loop -->
@for (int i = 0; i < users.size(); i++) {
<li>@users.get(i).getFirstName()</li>
}
<!-- standard 'for each' loop -->
@for (User user : users) {
<li>@user.getFirstName()</li>
}
<!-- for each loop with additional loop information
<li>0: John, first: true, last: false</li>
<li>1: Martin, first: false, last: false</li>
<li>2: Anna, first: false, last: true</li>
-->
@for ((i, user) : users) {
<li>@i.index(): @user.getFirstName(), first: @i.first(), last: @i.last()</li>
}
</ul>
}
The last loop is a special variation of Java's for-each loop. A second index parameter (named i in the example) can be used to access current iteration information.
Conclusion
Rocker can be definitively an option if you are looking for a Java template engine. According to the benchmark on the Rocker GitHub repository, Rocker is a lot faster than other Java template engines like Freemarker or Velocity. The comprehensive documentation is another good point to mention.
The fact that Rocker compiles to Java and allows you to pass data to views in a typesafe way is quite interesting. On one side this is useful because it helps to reduce bugs. On the other side, the same feature annoyed me a bit, while I was writing the examples. Whenever I made code changes that affected Rocker templates (e.g. by changing the name of a method that was called within a Rocker template) the templates stopped compiling. In some situations, this again stopped my Java code from compiling (because it was using the classes generated by Rocker). This is expected but it can break my preferred workflow of writing Java code first and fixing the templates later. Referencing templates via file names instead of using the generated classes solved this problem (but also throws away the type checking at compile time).
Published at DZone with permission of Michael Scharhag, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments