Last week I wrote about why you should switch to a templating engine from Java Server Pages. This week I’ll take it a step further and show you how to use FreeMarker, along with Bootstrap and RESTEasy, to create truly template driven websites. By adding a thin layer on top of FreeMarker, you’ll be able to theme your Java web apps like anything built on top of a CMS like WordPress or Drupal.

The Final Product

The example I’ll use here is a simple article submission website. Something like what you’d see on Javalobby or TheServerSide to add news and announcements. In this case, the app does only three things:

Displays submitted articles Accepts new article submissions Accepts confirmation for new article submissions

Bootstrap Theme

This web app uses Twitter’s Bootstrap front-end framework for it’s look and feel (theme). Since the app is template driven, you can swap out the four template files to completely re-theme it with another style.

Templates Files

All of the the app’s HTML output — forms, tables, links — are stored in four FreeMarker template files. These files can be located anywhere FreeMarker can access (see FreeMarker Template Loaders), but “WEB-INF/content” is a safe default to start with. When being used, the files will be referenced without the “WEB-INF/content” prefix, as “site.html” and “articles/form-article.html”. Here’s an overview of the files, you’ll see how they are used in the REST resources later.

# Template Description 1 WEB-INF/content/site.html Overall website template. Includes references to JS and CSS files. 2 WEB-INF/content/articles/grid-articles.html Article list table. 3 WEB-INF/content/articles/form-article.html New article entry form. 4 WEB-INF/content/articles/form-article-confirmation.html New article confirmation form.

site.html

WEB-INF/content/site.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"> <link href="/blog-article/css/articles.css" rel="stylesheet"> <title>${title!"Articles"}</title> </head> <body> <div class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Articles</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Home</a></li> <li><a href="#about">About</a></li> <li><a href="#contact">Contact</a></li> </ul> </div><!--/.nav-collapse --> </div> </div> <div class="container"> <div class="starter-template"> ${body} </div> </div><!-- /.container --> <script src="https://code.jquery.com/jquery.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script> </body> </html> 33 34 35 36 37 38 39 <div class = "container" > <div class = "starter-template" > ${body} </div> </div> <!-- /.container -->

This file houses the bulk of the HTML/FreeMarker code. It contains a body region — ${body} — where each page’s content (like grid-articles.html) will be injected. (Click the Expand Code icon in the toolbar to see the full source code.)

grid-articles.html

WEB-INF/content/articles/grid-articles.html <h2>Articles</h2> <div class="buttons"> <a id="addArticleButton" type="button" class="btn btn-primary" href="new/">Add Article</a> </div> <table class="table table-striped table-bordered table-hover"> <thead> <tr> <th>#</th> <th>Title</th> <th>Name</th> <th>Email</th> <th>URL</th> </tr> </thead> <tbody> <#list articles as article> <tr> <td>${article_index + 1}</td> <td>${article.title}</td> <td>${article.name}</td> <td>${article.email}</td> <td>${article.url}</td> </tr> </#list> </tbody> </table> 18 19 20 21 22 23 24 25 26 <#list articles as article > <tr> <td> ${article_index + 1} </td> <td> ${article.title} </td> <td> ${article.name} </td> <td> ${article.email} </td> <td> ${article.url} </td> </tr> </#list>

The grid template draws the article table using a combination of HTML and FreeMarker code. It expects a collection named “article” and uses the FreeMarker list directive to create a table row for each element it finds.

The actual “articles” collection can be Java beans, JSON objects, or any other kind of sequence or collection FreeMarker understands.

form-article.html

WEB-INF/content/articles/form-article.html <h2>Add New Article</h2> <form method="post" action="${addNewArticleFormAction!}"> <div class="form-group"> <input type="url" class="form-control" id="url" name="url" placeholder="Enter URL" required="" value="${(article.url)!}"> </div> <div class="form-group"> <input type="text" class="form-control" id="title" name="title" placeholder="Enter title" required="" value="${(article.title)!}"> </div> <div class="form-group"> <input type="email" class="form-control" id="email" name="email" placeholder="Enter email" required="" value="${(article.email)!}"> </div> <div class="form-group"> <input type="text" class="form-control" id="name" name="name" placeholder="Enter your name" required="" value="${(article.name)!}"> </div> <div class="buttons"> <button type="submit" class="btn btn-default btn-primary">Submit</button> <a type="submit" class="btn" href="..">Cancel</a> </div> </form> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <h2> Add New Article </h2> <form method = "post" action = "${addNewArticleFormAction!}" > <div class = "form-group" > <input type = "url" class = "form-control" id = "url" name = "url" placeholder = "Enter URL" required = "" value = "${(article.url)!}" > </div> <div class = "form-group" > <input type = "text" class = "form-control" id = "title" name = "title" placeholder = "Enter title" required = "" value = "${(article.title)!}" > </div> <div class = "form-group" > <input type = "email" class = "form-control" id = "email" name = "email" placeholder = "Enter email" required = "" value = "${(article.email)!}" > </div> <div class = "form-group" > <input type = "text" class = "form-control" id = "name" name = "name" placeholder = "Enter your name" required = "" value = "${(article.name)!}" > </div> <div class = "buttons" > <button type = "submit" class = "btn btn-default btn-primary" > Submit </button> <a type = "submit" class = "btn" href = ".." > Cancel </a> </div> </form>

The new article template renders the input form for new entries. On submit it does a POST back to it’s GET URL if no addNewArticleFormAction value is specified. It also looks for, and tries to use, the article’s properties (like article.url) if provided to the template.

form-article-confirmation.html

WEB-INF/content/articles/form-article-confirmation.html <form method="post" action="" class="form-horizontal"> <h2>Confirm Article Submission?</h2> <div class="form-group"> <label class="col-sm-2 control-label">URL</label> <div class="col-sm-10"> <p class="form-control-static">${article.url}</p> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Title</label> <div class="col-sm-10"> <p class="form-control-static">${article.title}</p> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <p class="form-control-static">${article.email}</p> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <p class="form-control-static">${article.name}</p> </div> </div> <div class="buttons"> <button type="submit" class="btn btn-default btn-primary">Confirm</button> <a type="submit" class="btn" href="../..">Cancel</a> </div> </form> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <form method = "post" action = "" class = "form-horizontal" > <h2> Confirm Article Submission? </h2> <div class = "form-group" > <label class = "col-sm-2 control-label" > URL </label> <div class = "col-sm-10" > <p class = "form-control-static" > ${article.url} </p> </div> </div> <div class = "form-group" > <label class = "col-sm-2 control-label" > Title </label> <div class = "col-sm-10" > <p class = "form-control-static" > ${article.title} </p> </div> </div> <div class = "form-group" > <label class = "col-sm-2 control-label" > Email </label> <div class = "col-sm-10" > <p class = "form-control-static" > ${article.email} </p> </div> </div> <div class = "form-group" > <label class = "col-sm-2 control-label" > Name </label> <div class = "col-sm-10" > <p class = "form-control-static" > ${article.name} </p> </div> </div> <div class = "buttons" > <button type = "submit" class = "btn btn-default btn-primary" > Confirm </button> <a type = "submit" class = "btn" href = "../.." > Cancel </a> </div> </form>

The confirmation template uses the supplied article to display it’s values, then POSTs back to it’s GET location on submit.

RESTful Resources

All page requests and form posts are handled by the RootResource class. RootResource can be though of as the controller in the model-view-controller pattern. It’s implemented as a JAX-RS resource running on JBoss’ RESTEasy framework.

POST requests always redirect the browser to a GET method on completion, while GET requests typically return content — complete HTML pages in this case. The interesting methods here are the GET handlers. They use the content API to interact with FreeMarker and link the model to the various template views.

RootResource.java package com.stackhunter.blog.example.articlesubmission.website; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.jboss.resteasy.annotations.Form; import com.stackhunter.blog.example.articlesubmission.model.Article; import com.stackhunter.web.content.Content; @Path("/") @Produces(MediaType.TEXT_HTML) public class RootResource { private static final List<Article> articles = new ArrayList<Article>(); private static final Map<String, Article> pendingArticles = new ConcurrentHashMap<String, Article>(); static { articles.add(new Article( "https://blog.stackhunter.com/2014/01/17/10-reasons-to-replace-your-jsps-with-freemarker-templates/", "10 Reasons to Replace Your JSPs With FreeMarker Templates", "support@stackhunter.com", "Dele Taylor")); articles.add(new Article( "https://blog.stackhunter.com/2014/01/14/how-to-load-config-files-with-the-strategy-pattern/", "How to Load Config Files with the Strategy Pattern", "support@stackhunter.com", "Dele Taylor")); articles.add(new Article( "https://northconcepts.com/blog/2013/01/18/6-tips-to-improve-your-exception-handling/", "6 Tips to Improve Your Exception Handling", "support@stackhunter.com", "Dele Taylor")); articles.add(new Article( "https://northconcepts.com/blog/2011/07/05/use-dynamic-proxies-to-create-a-simple-powerful-event-bus-part-1/", "Use dynamic proxies to create a simple, powerful event bus ", "support@stackhunter.com", "Dele Taylor")); } @GET @Path("/") public Response getHome() throws Throwable { return Response.seeOther(new URI("/articles/")).build(); } @GET @Path("/articles") public Content getArticles() throws Throwable { Content content = new Content("site.html"); content.add("body", new Content("articles/grid-articles.html")); content.add("articles", articles); return content; } @GET @Path("/articles/new") public Content getNewArticle() throws Throwable { Content content = new Content("site.html"); content.add("body", new Content("articles/form-article.html")); return content; } @POST @Path("/articles/new") public Response postNewArticle(@Form Article article) throws Throwable { String articleId = UUID.randomUUID().toString(); pendingArticles.put(articleId, article); return Response.seeOther(new URI("/articles/confirm/" + articleId + "/")).build(); } @GET @Path("/articles/confirm/{articleId}") public Content getConfirmNewArticle(@PathParam("articleId") String articleId) throws Throwable { Article article = pendingArticles.get(articleId); Content content = new Content("site.html"); content.add("title", "Confirm Article Submission"); content.add("body", new Content("articles/form-article-confirmation.html")); content.add("body", new Content("articles/form-article.html")); content.add("addNewArticleFormAction", "../../new/"); content.add("article", article); return content; } @POST @Path("/articles/confirm/{articleId}") public Response postConfirmNewArticle(@PathParam("articleId") String articleId) throws Throwable { Article article = pendingArticles.remove(articleId); articles.add(article); return Response.seeOther(new URI("/articles/")).build(); } } 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 @GET @Path ( "/" ) public Response getHome ( ) throws Throwable { return Response . seeOther ( new URI ( "/articles/" ) ) . build ( ) ; } @GET @Path ( "/articles" ) public Content getArticles ( ) throws Throwable { Content content = new Content ( "site.html" ) ; content . add ( "body" , new Content ( "articles/grid-articles.html" ) ) ; content . add ( "articles" , articles ) ; return content ; } @GET @Path ( "/articles/new" ) public Content getNewArticle ( ) throws Throwable { Content content = new Content ( "site.html" ) ; content . add ( "body" , new Content ( "articles/form-article.html" ) ) ; return content ; } @POST @Path ( "/articles/new" ) public Response postNewArticle ( @Form Article article ) throws Throwable { String articleId = UUID . randomUUID ( ) . toString ( ) ; pendingArticles . put ( articleId , article ) ; return Response . seeOther ( new URI ( "/articles/confirm/" + articleId + "/" ) ) . build ( ) ; } @GET @Path ( "/articles/confirm/{articleId}" ) public Content getConfirmNewArticle ( @PathParam ( "articleId" ) String articleId ) throws Throwable { Article article = pendingArticles . get ( articleId ) ; Content content = new Content ( "site.html" ) ; content . add ( "title" , "Confirm Article Submission" ) ; content . add ( "body" , new Content ( "articles/form-article-confirmation.html" ) ) ; content . add ( "body" , new Content ( "articles/form-article.html" ) ) ; content . add ( "addNewArticleFormAction" , "../../new/" ) ; content . add ( "article" , article ) ; return content ; } @POST @Path ( "/articles/confirm/{articleId}" ) public Response postConfirmNewArticle ( @PathParam ( "articleId" ) String articleId ) throws Throwable { Article article = pendingArticles . remove ( articleId ) ; articles . add ( article ) ; return Response . seeOther ( new URI ( "/articles/" ) ) . build ( ) ; }

Content API

The Content class is the central piece in that thin layer on top of FreeMarker I mentioned earlier. It allows multiple templates and/or values to be added to the same named slot (like ${body}) and it allows nested templates to find models added to their parent. It also handles all the boilerplate FreeMarker code. Its possibly the only class you’ll need to use templates in your own servlets or RESTful resources.

RootResource.getConfirmNewArticle(String) @GET @Path("/articles/confirm/{articleId}") public Content getConfirmNewArticle(@PathParam("articleId") String articleId) throws Throwable { Article article = pendingArticles.get(articleId); Content content = new Content("site.html"); content.add("title", "Confirm Article Submission"); content.add("body", new Content("articles/form-article-confirmation.html")); content.add("body", new Content("articles/form-article.html")); content.add("addNewArticleFormAction", "../../new/"); content.add("article", article); return content; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @GET @Path ( "/articles/confirm/{articleId}" ) public Content getConfirmNewArticle ( @PathParam ( "articleId" ) String articleId ) throws Throwable { Article article = pendingArticles . get ( articleId ) ; Content content = new Content ( "site.html" ) ; content . add ( "title" , "Confirm Article Submission" ) ; content . add ( "body" , new Content ( "articles/form-article-confirmation.html" ) ) ; content . add ( "body" , new Content ( "articles/form-article.html" ) ) ; content . add ( "addNewArticleFormAction" , "../../new/" ) ; content . add ( "article" , article ) ; return content ; }

Let’s breakdown the getConfirmNewArticle method to get a better idea of how things work.

The outer website template is created (line 6). This template will hold data and other templates. It will evaluate itself and any nested templates in the right order sometime after line 14. You can force a manual evaluation by calling it’s toString method. Two templates are nested into the outer template inside it’s body region (lines 9 & 10). The pending article is added to the outer template as a variable named article (line 12). The outer template is returned, evaluated, and it’s string contents sent to the browser (automatically, after line 14).

There are several other classes in the API. They are used internally and exposed to keep the API flexible for special cases. The ITemplateSource, for example, is a factory for creating FreeMarker Templates. In most cases calling the Content constructor with the file path argument will suffice.

Template Driven Websites

Template driven websites plus RESTful resources is the sweet spot for Java web development. Not only do you gain the ability to theme and re-theme your web application, you also get: