Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Thymeleaf With JavaEE 8

DZone's Guide to

Thymeleaf With JavaEE 8

Learn how to configure the open source tool Thymeleaf as a view technology with JavaEE 8.

· Java Zone
Free Resource

Never build auth again! The Okta Developer Platform makes it simple to implement authentication, authorization, MFA and more in Java applications. Get started with the free API.

Background

In our organization, (and so as for many others, I believe), Apache Tiles had been a go to framework for rendering template based views. And it worked great with Spring. In recent years, thymeleaf has evolved as a mature and feature rich view technology.

Few weeks ago, I started evaluating Thymeleaf as a replacement for Apache Tiles and loved it from the start. Although there are several articles for Thymeleaf + Spring, there aren't many for JavaEE; thus decided to write one myself, for those who are interested.

This article aims to provide a brief introduction about configuring Thymeleaf as a view and templating technology with MVC 1.0 specified in JavaEE 8.

The Groundwork

web.xml

Here is the simple web.xml. Since we will be using annotation based configuration, there is not much to include here.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

</web-app>

beans.xml

There is nothing much to this file either, except bean-discovery-mode is set to 'all'. This will ensure that application server configures all the annotated beans.

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
    bean-discovery-mode="all">

</beans>

Libraries

DZone: Programming & DevOps news, tutorials & tools

We would primarily need libraries for MVC 1.0 specification and its reference implementation along with Thymeleaf core ones. Please find links to these libraries in the reference section at the end of the article.

Libraries

Setting up everything ...

Application Setup

First step is to configure our JAX-RS application. Create an Application class (or any other one as you like) annotated with @ApplicationPath. The argument to this annotation will become the application path after deployment.

@ApplicationPath("api")
public class Application extends javax.ws.rs.core.Application
{
private static final Logger logger = LogManager.getLogger(Application.class);
}

Controller Setup

If you are familiar with JAX-RS, setting up MVC 1.0 controller won't be much of a revelation. There are couple of things to notice -

  1. Container can inject models (similar to other resources). Controller methods can set the model attributes similar to that of the HashMap

  2. Annotate your class with @Controller. This annotation is provided by the MVC 1.0 specification which tells container to treat this class as a MVC controller.  

Following is the code for a Controller for serving a login page -

@Path("login")
@Controller
public class LoginController
{
@Inject
Models models;

private static final Logger logger = LogManager.getLogger(LoginController.class);

@GET
@Produces(MediaType.TEXT_HTML)
public String viewLogin()
{
models.put("company", "Acme Products");
models.put("product", "JavaEE 8 Bootstrap");
return "login";
}
}

viewLogin method returns a string "login", indicating that a view named "login" is to be  served. 

Thymeleaf Setup

Following code shows how to configure Thymeleaf for serving the content based on the "view" returned from the controller. 

@ApplicationScoped
public class ThymeleafViewEngine
extends ViewEngineBase
{

@Inject
private ServletContext servletContext;

private TemplateEngine engine;

@Override
public boolean supports(String view)
{
return !view.contains(".");
}

@PostConstruct
public void postConstruct()
{
TemplateResolver resolver = new ServletContextTemplateResolver();

resolver.setPrefix("/WEB-INF/layouts/");
resolver.setSuffix(".xhtml");
resolver.setTemplateMode(StandardTemplateModeHandlers.XHTML.getTemplateModeName());
resolver.setCacheable(false);

engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
}

@Override
public void processView(ViewEngineContext context) throws ViewEngineException
{
try
{
HttpServletRequest request = context.getRequest();
HttpServletResponse response = context.getResponse();
WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());

ctx.setVariables(context.getModels());
request.setAttribute("view", context.getView());

engine.process(context.getView().equals("login") ? "login-template":"default-template", ctx, response.getWriter());
}
catch (IOException e)
{
throw new ViewEngineException(e);
}
}
}


Here is what the above code does -

  1. The class extends ViewEngineBase and is annotated as @ApplicationScoped. This enables this class to be treated as MVC ViewEngine and used when a controller returns a view.

  2.  postConstruct() method initializes Thymeleaf template engine with template directory and template mode is set as HTML5 
  3. For production environment, resolver.setCacheable(true) is recommended.
  4. processView() method is called when a controller returns a view, in this case "login" view.
  5. Thymeleaf templates can't access MVC model directly, thus the model is copied to the Thymeleaf context using ctx.setVariables(context.getModels());
  6. In case of Apache Tiles, single view represents a template and its sub components using a XML configuration. To achieve this with Thymeleaf, the view returned by the controller is used to set the template to be used. Thus with context.getView().equals("login") ? "login-template" : "default-template", we configure login-template for "login" view and default-template for rest of the views.
  7. We then set view name as a request attribute so that we can tell the template where to look for its view specific content components. 

Setting up the view template

Following code is a typical template for rendering a view. The template used scoped variable ${view} to substitude the value of "view" in the template.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Bootstrap</title>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

<!-- Favicon -->

<link rel="icon" type="image/x-icon" th:href="@{/images/favicon.ico}" />

<!-- Common styles -->

<link rel="stylesheet" type="text/css" th:href="@{/styles/common.css}" />
<link rel="stylesheet" type="text/css" th:href="@{/styles/form.css}" />

<!-- View specific styles -->

<link rel="stylesheet" type="text/css" th:href="@{/styles/login-layout.css}" />

<th:block th:include="${view} :: styles" />

<!-- Common scripts -->

<script type="text/javascript" th:src="@{/scripts/jquery-2.1.3.min.js}"></script>
<script type="text/javascript" th:src="@{/scripts/velocity.min.js}"></script>
<script type="text/javascript" th:src="@{/scripts/commonts.js}"></script>
<script type="text/javascript" th:src="@{/scripts/form.js}"></script>

<!-- View specific scripts -->

<script type="text/javascript" th:src="@{/scripts/login.js}"></script>

<th:block th:include="${view} :: scripts" />

</head>
<body>

<div class="body">
<div class="page">

<div class="branding">

<div class="company-branding" th:text="${company}">Some Default Company</div>
<div class="product-branding" th:text="${product}">Bootstrap</div>

</div>

<div th:replace="${view} :: page">
</div>
</div>
</div>
</body>
</html>


The directory structure for web content is as below -

Image title

Setting up the "view"

Login view (login.xhtml) specifies various content components which will be included by the template while rendering full page.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<head th:fragment="styles">
</head>

<head th:fragment="scripts">
</head>

<body th:fragment="page">

<form class="login-form" name="login-form" method="post" th:action="@{/login.json}">

<h2 class="form-header">Customer Login</h2>

<div class="form-body">
<div class="field-wrapper">
<label><input type="text" name="user-name" value="" placeholder="User Name" autocomplete="off" /></label>
</div>
<div class="field-wrapper">
<label><span /> <input type="password" name="password" value="" placeholder="Password" autocomplete="off" /></label>
</div>
</div>

<div class="form-options">
<a th:href="@{/forgot-password}">Forgot Password</a>
<a th:href="@{/sign-up}">Sign Up</a>
</div>

<div class="form-footer">
<input type="submit" value="Login" />
<input type="reset" value="Cancel" />
</div>
</form>

</body>
</html>


Deployment

We will deploy our application on glassfish as it is the only application server that supports the MVC 1.0 draft specification and comes bundled with its reference implementation. Once the application is deployed, go to http://localhost:8080/JavaEE/api/login for viewing the login page (assuming your application uses JavaEE as context path).

Suggestions ?

Please leave a comment for any suggestion.

Source Code

Please visit https://github.com/kedar-joshi/Thymeleaf-JavaEE-MVC for the sources

References

  1. For basic project structure https://dzone.com/articles/java-ee-8-mvc-getting-started-with-ozark

  2. For JavaEE 8 reference https://javaee8.zeef.com/arjan.tijms

  3. For MVC 1.0 basics http://www.bennet-schulz.com/2015/10/javaee-mvc-controllers.html and http://www.mscharhag.com/java-ee-mvc/a-detailed-look-on-mvc-controllers

  4. MVC 1.0 reference homepage https://ozark.java.net/

  5. For Thymeleaf download and documentation http://www.thymeleaf.org/download.html and http://www.thymeleaf.org/doc/articles/layouts.html

  6. For Thyemleaf custom layouts http://blog.codeleak.pl/2013/11/thymeleaf-template-layouts-in-spring.html

Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
javaee8 ,thymeleaf

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

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

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}