Pages

Thursday, November 10, 2016

Template Engines at One : Spring-Boot and Engines setup review after Deprecated Velocity.


  Let’s dive for while into the Template engines problematics of MVC based frameworks. 

  In next minutes the text will brings you to the mystery of different templating possibilities supported by Spring Boot framework.   

  Spring Boot became to be very popular because of its configuration possibilities  and full support of spring based application. Spring Boot follows the simple moto “just run”, wait a sec, no, it’s not NIKE. In the age of “Micro-Services” and cutting monoliths this motto, in     Spring way, has quite nice impact on desired application prototyping.  

  I don’t think there is any necessity to go much deeper into the Model View Controller (MVC) design pattern issue because there is a mass of other articles which can be easily found. 
  The main intent of the following text is to review the setups of the different Java Template Engines for Spring based application. How can even such question come into the focus ?   
  Well, the reason is that Velocity Engine is deprecated for a while and a lot developers around the globe need to find well fitting alternative.
  Let’s begin and define the set for our test. We put into the comparison following engines:

  1. Apache Velocity
  2. Apache FreeMarker 
  3. Thymeleaf
  4. Pebble
  I’ve not included JSP Engine because JSPs are mature technology and has been around since the early days, which means that thousands and more articles has been already written around. The fact that JSPs are really hard to beat in case of raw speed stays but this is not in the focus now.

  By having prepared MvcConfiguration class which 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 mentioned template engines has under the folder webapp its own directory dedicated only to it. Such directory (velocity, freemarker, thymeleaf and pebble) contains only engine related files.
  And here is the Deprecated engine that has been widely used over last several years: 

Apache Velocity 
   Apache Velocity Template Engine is used for the comparison and also to make testing other three alternatives (FreeMarker, Thymeleaf and Pebble) little bit simpler. Apache Velocity is one of Jakarta project. Each of Velocity templates is processed but not compiled to Java which supports 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 will be 
used based 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 simple velocity template. We call the file test.vm and it will contain 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 has been used configuration file called application.properties located inside the project resources folder. 
In velocity case it will contain loader path setup (ps: you can customise it)
spring.velocity.resourceLoaderPath=/velocity/
  Congratulation!  Deprecated Template Engine Velocity us UP and Running, but this is not all what we want to achieve so we continue with the next alternative:

Apache FreeMarker
  The 1st considered candidate as the replacement to Velocity is the FreeMarker. FreeMarker is currently coming from the Apache projects incubator supporeted by Apache Software Foundation (ASF). ASF puts its effort to support FreeMarker development, which is very good sign, for long life. The one more reason may be that FreeMarker is widely used across the Apache family projects, 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 ready for the simple FreeMarker templates. Inside the webapp folder we create new folder called freemarker and we add 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>
and magic.ftl file which will contains 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>
but hold on it’s not enough in case of FreeMarker we can not forget to properly add configuration inside the application.properties file:
spring.freemarker.templateLoaderPath=/freemarker/
  Now it’s done we have FreeMarker up and running inside our sample project!
Well done and we can move to the next one which is:

Pebble Template Engine
  It’s quite new player on the market. It promises quite useful inheritance features and easy to read syntax but comment this is out of 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 need to again configure ViewResolver properly. In case of Pebble, all is slightly more complicated because the result of the configuration is extremely closely connected with the Servlet config it self. 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;
}
  Previous 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 @Bean
@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 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 ready, Pebble is up and running and we can go directly to the last option: 

Thymeleaf Template Engine
  Thymeleaf present itself as the ideal choice for HTML5 JVM web development, it may be true but it’s over 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 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 the 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 into 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;
}
...
  As the last step we create again under what webapp folder new folder called thymeleaf and 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 big applause 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 engines has assigned its own @Controller which is responsible for proper output generation: 

1.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";
    }

}
2. 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";
    }

}
3. Pebble
@Controller
public class PebbleHelloController {

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


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

}

Configuration experience Summary
  Now is the right time to write 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 option in parallel, but as has been mentioned before doing right choice is out of the scope of this article. 
I've created sample gradle project which imports all Temple engines starter. This setup can be find 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 sample project in testing yours choice !!!


used versions:
Java JDK: 1.8.111
Spring-Boot:  1.4.1.RELEASE

Pebble: 2.2.3

No comments: