Introduction
I already wrote about a web templating system named Tiles. This leaded me to search for other java template systems, and I stumbled across StringTemplate.
StringTemplate is a template engine that defines its own language grammar. It enforce the separation of the display logic from the business logic, in example by disallowing variable assignments in that display logic. As an analogy, JSP, even if correctly used with JSTL and a separated controller, will always make it possible for programmers to modify the model, call a service, query a database etc. StringTemplate forbids any assignments, making it harder to put what should be business or controller logic in the view.
Here is a brief example of using StringTemplate in a j2ee environment. It is not meant to be a real life example, but more a proof of concept. The example consist of a welcome and about page, each one using the same layout.
Configuration
dependencies
There is not much dependencies to add to be able to use StringTemplate :
- stringtemplate.jar
- antlr-2.7.7.jar
I took those files in the lib directory of the StringTemplate 3.2 distribution. They are in the /WEB-INF/lib directory of my web application.
Servlet (web.xml)
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>StringTemplateServlet</servlet-name>
<servlet-class>sample.StringTemplateServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StringTemplateServlet</servlet-name>
<url-pattern>*.st</url-pattern>
</servlet-mapping>
</web-app>
Implementation
The servlet
package sample;
import javax.servlet.*;
import javax.servlet.http.*;
import org.antlr.stringtemplate.*;
import java.io.*;
public class StringTemplateServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
ServletContext context = getServletContext();
String templatePath = context.getRealPath("/WEB-INF");
StringTemplateGroup group = new StringTemplateGroup("underwebinf", templatePath);
// removes the leading "/" and the trailing ".st"
String servletPath = request.getServletPath().substring(1,request.getServletPath().lastIndexOf(".st"));
StringTemplate page = group.getInstanceOf(servletPath);
out.print(page);
}
}
The servlet specify that all template files will be found under the WEB-INF directory. The name of the .st file that will be called is the one specified in the request. In example, the URL http://localhost:8080/stringtemplate/home.st would point to the /WEB-INF/home.st template file.
The home page (/WEB-INF/home.st)
The home page represent a view in itself. It must use the template layout :
$defaultLayout(body=welcome())$
This single line of StringTemplate code tells StringTemplate to execute defaultLayout.st, and to replace it’s $body$ attribute with the result of the welcome.st template file execution. I can therefore reference other templates by their name, stripping the “.st” part.
The layout (/WEB-INF/layout.st)
The general layout is made to be shared by web pages. It defines the general look of a page :
<html>
<body style="width:100%;height:100%">
<table border="1" cellspacing="0" cellpadding="0" style="width:100%;height:100%">
<tr>
<td colspan="2">
$header()$
</td>
</tr>
<tr>
<td>
$menu()$
</td>
<td>
$body$
</td>
</tr>
<tr>
<td colspan="2">
$footer()$
</td>
</tr>
</table>
</body>
</html>
Again, this template will call another template named header.st. It will do the same for the footer and the menu : each of these layout part is defined in a separate file. The $body$ expression tells the template that it must receive the body as an attribute (see the home.st above).
The welcome body (/WEB-INF/welcome.st)
This is the body of the home page defined in home.st :
<div><h1>Hello, dude !!!</h1></div>
The Header (/WEB-INF/header.st)
<div><h1>this is the header</h1></div>
The Menu (/WEB-INF/menu.st)
<div>
<h1>
<ul>
<li>Menu item 1</li>
<li>Menu item 2</li>
<li>Menu item 3</li>
<li>Menu item 4</li>
<li>Menu item 5</li>
<li>Menu item 6</li>
</ul>
</h1>
</div>
The Footer (/WEB-INF/footer.st)
<div><h1>this is the footer</h1></div>
Compiling and launching the web application under tomcat, and then calling http://localhost:8080/stringtemplate/home.st produces the following output :

With the layout already defined, coding the about page becomes easy : i must create the about page itself, and the body of that page. Let’s start by the page which call the layout :
The about page (/WEB-INF/about.st)
$layout/defaultLayout(body=company_description())$
And the body of that page (WEB-INF/company_description.st)
<h1>About us</h1> Lorem ipsum dolor sit amet, consectetur adipiscing elit. <br/> Morbi tempus mauris condimentum arcu. Nunc in quam. Vivamus<br/> quis quam sed tortor euismod pellentesque. Nulla lobortis. <br/> Donec urna metus, adipiscing vel, iaculis vel, congue at, odio. <br/> Nullam et dolor id tortor tempor gravida. Curabitur eleifend<br/>
I now have two pages that shares the same layout. It would be easy for an entire application to reuse that layout without suffering of a “copy and paste” madness.
Now that all is working, let’s play with the files a little bit. The first thing I’d like to do is to put my layout and its parts in a separate folder. Moving them in WEB-INF/layout force me to modify the two pages :
home.st
$layout/defaultLayout(body=welcome())$
about.st
$layout/defaultLayout(body=company_description())$
Adding to this, I also must modify the layout file (/WEB-INF/layout/defaultLayout.st). This is because, calling $header()$, $menu()$ or $footer()$ will no longer be possible. I have to call all these parts using the correct leading path : $layout/header()$, $layout/menu()$, $layout/footer()$. So here is the new listing of defaultLayout.st :
layout/defaultLayout.st
<html>
<body style="width:100%;height:100%">
<table border="1" cellspacing="0" cellpadding="0" style="width:100%;height:100%">
<tr>
<td colspan="2">
$layout/header()$
</td>
</tr>
<tr>
<td>
$layout/menu()$
</td>
<td>
$body$
</td>
</tr>
<tr>
<td colspan="2">
$layout/footer()$
</td>
</tr>
</table>
</body>
</html>
Doing this made me realize one thing : always reference a template using the complete path starting from the directory associated with the StringTemplateGroup in action (see the servlet code). Here my template group as been associated with the WEB-INF directory of the application, making all my template references relative to this directory.
Accessing scoped variables.
In a JSP solution, I would have access to a page, request, session and application scopes in which I can read variables. My basic implementation of the StringTemplateServlet doesn’t make it possible for the template files to access scopes. Let’s modify the servlet so I can access request and session variables from my template files :
package sample;
import javax.servlet.*;
import javax.servlet.http.*;
import org.antlr.stringtemplate.*;
import java.io.*;
import java.util.*;
public class StringTemplateServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
ServletContext context = getServletContext();
String templatePath = context.getRealPath("/WEB-INF");
StringTemplateGroup group = new StringTemplateGroup("underwebinf", templatePath);
// removes the leading "/" and the trailing ".st"
String servletPath = request.getServletPath().substring(1,request.getServletPath().lastIndexOf(".st"));
StringTemplate page = group.getInstanceOf(servletPath);
page.setAttribute("session", getSessionMap(request.getSession()));
page.setAttribute("request", getRequestMap(request));
request.getSession().setAttribute("user", "dude");
out.print(page);
}
private Map getSessionMap(HttpSession session) {
Map sessionMap = new HashMap();
for(Enumeration names = session.getAttributeNames();names.hasMoreElements();) {
String attributeName = (String) names.nextElement();
sessionMap.put(attributeName, session.getAttribute(attributeName));
}
return sessionMap;
}
private Map getRequestMap(HttpServletRequest request) {
Map requestMap = new HashMap();
for(Enumeration names = request.getAttributeNames(); names.hasMoreElements();) {
String attributeName = (String) names.nextElement();
requestMap.put(attributeName, request.getAttribute(attributeName));
}
return requestMap;
}
}
Each scope (request and session) is represented by a map placed among the template’s attributes. Now the welcome file can access the newly created session scoped user attribute :
/WEB-INF/welcome.st
<div><h1>Hello, $session.user$ !!!</h1></div>
All executed templates in the chain could have used $session.user$ to access the “user” session variable. As an example, it would have been possible to print the current user on each page, as part of the layout.
Conclusion
StringTemplate can play the role of the view in a J2EE web application, and can be used as a replacement for JSP/JSTL. It is crafted to answer the need of strict separation between the business and the display logic.
This example was little compared to what this API can do. I encourage those who read my post to take a look at the StringTemplate documentation…. But I’m sure you already did ;)
Hi Richard,
Thanks a lot for this tutorial… The lines 12 and 13 from the Implementation section saved me!!!
I’ve spent 4 days seeking for this solution. I’m a PHP programmer starting Java development and I’m developing a JSP framework.
Congrats!
Added to my delicious!
Thanks a lot!
Cya!
Comment by Ramiro Varandas Jr — March 29, 2009 @ 6:57 am |
Great site this richardbarabe.wordpress.com and I am really pleased to see you have what I am actually looking for here and this this post is exactly what I am interested in. I shall be pleased to become a regular visitor :)
Comment by Updapysless — April 5, 2009 @ 5:18 pm |
hi this is very nice document i would implement it in my site thanks.
ravendra
Comment by ravendra kumar — June 11, 2009 @ 3:29 pm |
Thank you for your nice tutorial. It really helps me make maintainable template web sites.
Thanks
Sungho
Comment by Sungho Maeung — July 20, 2009 @ 11:08 am |