Over a million developers have joined DZone.

A Review of Java Template Engines

In this article, Miro Kopecky provides a thorough review of Java template engines Apache Velocity, Apache FreeMarker, Thymeleaf, and Pebble.

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Let’s dive for while into the template engines problematics of MVC based frameworks. In this article, you will learn about the mystery of different templating possibilities supported by Spring Boot framework.   

Spring Boot has become very popular because of its configuration possibilities and full support of spring based application. In the age of microservices and cutting monoliths, this motto of "just run" has quite a nice impact on the desired prototyping application.  

I don’t think it's necessary to go much deeper into the Model View Controller (MVC) design pattern issue because there are many other articles in which this can be easily found. 

The main intent of this article is to review the setups of the different Java template engines for Spring-based applications. How can even such a question even arise?   

Well, the reason is that Velocity Engine has been deprecated for a while and a lot developers around the globe need to find well-fitting alternatives.

Let’s begin and define the set for our test. We will compare Apache VelocityApache FreeMarkerThymeleaf, and Pebble.

I have not included JSP Engine because JSPs are mature technologies and have been around since the early days, which means that many articles have been already written about it. The fact that JSPs are really hard to beat in the case of raw speed remains, but this is not in the focus now.

Prepare the MvcConfiguration class that extends WebMvcConfigurerAdapter:

@Configuration
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
...

The mentioned MvcConfiguration class must define @Bean ViewResolver that can negotiate about the proper request ViewResolver.

@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
        List resolvers = ...

Each of the mentioned template engines has, under the folder webapp, its own directory dedicated only to it. Such directories (Velocity, Freemarker, Thymeleaf and Pebble) contain only engine-related files.

Here is the deprecated engine that has been widely used over last several years.

Apache Velocity 

Apache Velocity Template Engine is used for comparison and also to make testing the other three alternatives (FreeMarker, Thymeleaf, and Pebble) a little bit simpler. Apache Velocity is one of the Jakarta projects. Each of the Velocity templates is processed but not compiled to Java, which supports a better code separation.

Following code snippets configures Spring Boot ViewResolver and enables the Velocity usage:

@Bean(name = "velocityViewResolver")
public ViewResolver getVelocityViewResolver() {
   VelocityViewResolver resolver = new VelocityViewResolver();
   resolver.setSuffix(".vm");
   resolver.setCache(true);
   return resolver;
}

Having configured ViewResolver we need to add it to the contentNegotiatingViewResolver @Bean, which gives us the access to the ContentNegotiationManager.

The ContentNegotiationManager provides look-up methods to the file extensions based on MediaType. In the example case, it will be used for specific engine file suffix search:

@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
   List resolvers =
      Arrays.asList(getVelocityViewResolver(),
      ...

Inside the directory webapp, we create directory velocity, and a simple velocity template. We call the file test.vm. It contains the following content:

<html lang="en">
<head>
   <title>Test Velocity</title>
</head>
<body>
<h2>This is $test</h2>
</body>
</html>

We are almost done. There is only one more important thing for setting up specific Spring Boot application properties that have been used a configuration file called application.properties located inside the project resources folder. In the velocity case, it will contain loader path setup (you can customize it).

spring.velocity.resourceLoaderPath=/velocity/

Congratulations! Deprecated Template Engine Velocity is up and running, but this is not all that we want to achieve. We continue with the next alternative.

Apache FreeMarker

The first considered candidate as the replacement to Velocity is the FreeMarker. FreeMarker is currently coming from the Apache projects incubator supported by Apache Software Foundation (ASF). ASF puts its effort to support FreeMarker development, which is a very good sign for a long life. One more reason may be that FreeMarker is widely used across the Apache family projects, a good example is newly accepted NetBeans one!

Let’s add FM support to sample project by configuring ViewResolver in the following way:

@Bean(name = "freeMarkerViewResolver")
public ViewResolver getFreeMakerViewResolver() {
   FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
   resolver.setSuffix(".ftl");
   resolver.setCache(true);
   return resolver;
}

We need to add also properly FreeMarker ViewResolver to the ContentNegotiationManager inside the MvcConfiguration @Bean:

@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
   List resolvers =
      Arrays.asList(getVelocityViewResolver(),
                    getFreeMakerViewResolver(),
                    ...

Now is the sample application is ready for the simple FreeMarker templates. Inside the webapp folder we a create new folder called freemarker and we add the following two files: index.ftl.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test applicaiton</title>
</head>
<body>
<h2>This is test application Main sample site</h2>
</body>
</html>

The magic.ftl file will contain simple FM tags:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Magic Spell: ${word}!</title>
</head>
<body>
<h2>MagicHappens by word: ${word}!</h2>
</body>
</html>

Hold on; it’s not enough in the case of FreeMarker we can not forget to properly add a configuration inside the application.properties file:

spring.freemarker.templateLoaderPath=/freemarker/

Now we have FreeMarker up and running inside our sample project! Well done. We can move to the next one.

Pebble Template Engine

It’s quite a new player on the market. It promises quite useful inheritance features and easy-to-read syntax but I will not talk about it because it would be beyond the scope of this article. This article is focused on ViewResolver Configuration and having it up and running as the motto of Spring Boot. As the first step, we again need to configure ViewResolver properly. In the case of Pebble, all is slightly more complicated because the result of the configuration is extremely closely connected to the Servlet config itself. Let’s see, we go back again in the @Bean MvcConfiguration and we add:

@Bean(name="pebbleViewResolver")
public ViewResolver getPebbleViewResolver(){
   PebbleViewResolver resolver = new PebbleViewResolver();
   resolver.setPrefix("/pebble/");
   resolver.setSuffix(".html");
   resolver.setPebbleEngine(pebbleEngine());
   return resolver;
}

Previously has been mentioned that Template may support configuration by application.properties file, this is currently not the case of Pebble. We need to configure all manually and we need to define more Pebble-related @Beans:

@Bean
public PebbleEngine pebbleEngine() {
  return new PebbleEngine.Builder()
                .loader(this.templatePebbleLoader())
                .extension(pebbleSpringExtension())
                .build();
}

@Bean
public Loader templatePebbleLoader(){
   return new ServletLoader(servletContext);
}

@Bean
public SpringExtension pebbleSpringExtension() {
  return new SpringExtension();
}

As you can see, the templatePebbleLoader @Bean requires direct access to the ServletContext which needs to be injected into the configuration @Bean

@Autowired
private ServletContext servletContext;
...

It also means that by doing this Pebble takes over the any created servlet and will play default choice when any other exists. This may not be bad but when you want to use Pebble and for example Thymeleaf together, you need to do slightly more Spring hacking.

Now we have prepared Pebble configuration, so let’s create a new Pebble folder under the webapp and add a new template file pebble.html

<html>
<head>
    <title>{{ pebble }}</title>
</head>
<body>
{{ pebble }}
</body>
</html>

Now we are finished, Pebble is up and running and we can go directly to the last option.

Thymeleaf Template Engine

Thymeleaf presents itself as the ideal choice for HTML5 JVM web development, it may be true but it’s beyond the scope of this article and you can try this claim by using the example project over my GitHub account. Thymeleaf has better Spring support  than Pebble. This allows us to use for its configuration the application.properties file and add Thymeleaf setup options there:

spring.thymeleaf.prefix=/thymeleaf/
spring.thymeleaf.suffix=.html

But the rest is very similar to Pebble:

@Bean(name = "thymeleafViewResolver")
public ViewResolver getThymeleafViewResolver() {
  ThymeleafViewResolver resolver = new ThymeleafViewResolver();
  resolver.setTemplateEngine(getThymeleafTemplateEngine());
  resolver.setCache(true);
  return resolver;
}

Thymeleaf takes similarly control over any new Servlet creation as you can see in MvcConfiguration @Bean

@Bean(name ="thymeleafTemplateEngine")
public SpringTemplateEngine getThymeleafTemplateEngine() {
  SpringTemplateEngine templateEngine = new SpringTemplateEngine();
  templateEngine.setTemplateResolver(getThymeleafTemplateResolver());
  return templateEngine;
}

@Bean(name ="thymeleafTemplateResolver")
public ServletContextTemplateResolver getThymeleafTemplateResolver() {
  ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
  templateResolver.setPrefix("/thymeleaf/");
  templateResolver.setSuffix(".htm");
  return templateResolver;
}

Now it’s time to add ViewResolver to the content negotiation configuration: 

@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
   List resolvers =
      Arrays.asList(getVelocityViewResolver(),
                    getFreeMakerViewResolver(),
//                  getPebbleViewResolver()
                    getThymeleafViewResolver()
                );
      ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
      resolver.setViewResolvers(resolvers);
      resolver.setContentNegotiationManager(manager);
      return resolver;
}
...

For the last step, we will create again under that webapp folder and new folder called thymeleaf. We add thyme.htm file there:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Thymeleaf</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'HERE IS, ' + ${thyme} + '!'" />
</body>
</htm>

And congratulations you have successfully configured all four Spring Boot Supported Template Engines. 

At the end of the configuration section, it is important to point out that each of the engines has assigned its own @Controller which is responsible for a proper output generation.

Velocity Controller 

@Controller
public class VelocityHelloController {

    @RequestMapping(value = "/velocity")
    public String test(Model model){
        System.out.println("Test");
        model.addAttribute("test", "Here is Velocity");
        return "test";
    }
}

FrameMarker

@Controller
public class FMHelloController {


    @RequestMapping("/")
    public String index(){
        System.out.println("INDEX");
        return "index";
    }

    @RequestMapping("/magic")
    public String magic(Model model, @RequestParam(value = "word", required=false, defaultValue="MagicHappens") String word) {
        System.out.println("MAGIC");
        model.addAttribute("word", word);
        return "magic";
    }
}

Pebble

@Controller
public class PebbleHelloController {

    @RequestMapping(value = "/pebble")
    public String something(Model model){
        System.out.println("Pebble");
        model.addAttribute("pebble", "The Pebble");
        return "pebble";
    }
}

Thymeleaf

@Controller
public class TLHelloController {


    @RequestMapping(value = "/thyme")
    public String something(Model model){
        System.out.println("Thymeleaf");
        model.addAttribute("thyme", "The Thymeleaf");
        return "thyme";
    }

}

Summary

Now is the right time to write a few last words about the general feeling from all mentioned possibilities. I don’t want to highlight any of those tested choices as the best replacement to the Deprecated Velocity Template Engine but from the configuration experiences and Spring framework support, I’d choose FrameMarker. By choosing FreeMarker, I won’t be limited in using Velocity and any other options in parallel, but as has been mentioned before, making the right choice is beyond the scope of this article. 

I have created a sample Gradle project that imports all Temple engines starter. This setup can be found inside the configuration file build.gradle.

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-freemarker:$springBootVersion")
    compile("org.springframework.boot:spring-boot-starter-velocity:$springBootVersion")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion")
    compile("com.mitchellbosecke:pebble-spring-boot-starter:$pebbleVersion")
    testCompile "junit:junit:${junitVersion}"
}

Enjoy the https://github.com/mirage22/spring-boot-freemaker-demosample project in testing!

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
springboot ,java ,web dev ,apache

Published at DZone with permission of Miro Wengner. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}