FreeMarker: An open alternative to JSP

How the template-based, open source API FreeMarker trumps JSP

1 2 3 Page 2
Page 2 of 3
public class ClientOrderServlet extends HttpServlet {
   public void doGet(HttpServletRequest req, HttpServletResponse res) 
   throws ServletException, IOException {
      //typical setup stuff
      res.setContentType("text/html");
      PrintWriter out = res.getWriter();
      //Any object without a parent that implements
    //TemplateModelRoot can be considered the ModelRoot
      SimpleHash modelRoot = new SimpleHash();
      //this will contain the list of orders
      SimpleList orderList = new SimpleList();
      //each order has a date and an id - we need a hash for this.
      //Let's assume this user has 2 orders
      SimpleHash order1 = new SimpleHash();
      order1.put("date", "1/1/00");
      order1.put("id", "12345");
      //represents 2nd order
      SimpleHash order2 = new SimpleHash();
      order2.put("date", "1/2/00");
      order2.put("id", "67890");
      //put the orders in the list
      orderList.add(order1);
      orderList.add(order2);
      //put the list on the ModelRoot
      modelRoot.put("orders", orderList);
      /* Wait, what's all this?  See below... */
      Template template = null;
      //this file may or may not contain FreeMarker tags
      String templatePath = "javaworld.template";
      try {
         template = new Template(templatePath);
      } catch (IOException e) {
       throw new ServletException(e);
      }
    
      // Process the template.
      template.process(modelRoot, out);
      out.close();
   }//method doGet
}//class ClientOrderServlet

The above code creates a ModelRoot with a list of hashes. Each hash contains two scalars, keyed by "date" and "id." The values associated with those keys are the fruit of the tree, and you can bet that they are the elements that the template is going to want to extract. Take note of the string literals used when putting the list and scalar elements into the SimpleHash object ("date," "id," "order"). These are the exact names that will be required when the template processor attempts to extract the values based on the tags placed in the template. A visual representation of the ModelRoot generated by the above code is shown in Figure 2.

Figure 2. The ModelRoot produced by ClientOrderServlet

So what are those last 13 lines all about in the previous listing? That code is neither model- nor view-related. It initializes and utilizes the controller part of the MVC pattern, and for small projects, it is all the controller code you will ever have to worry about. Much like JSP, FreeMarker compiles templates to improve runtime performance. Those templates are stored in memory in a format that leaves placeholders for dynamic data. The controller fills those placeholders with dynamic data from the model. For this purpose, the controller must have access to both the model and the view. Once the dynamic data is retrieved from the model, the controller will print the template to a given output stream. In the previous code, the template is a file called "javaworld.template." The Template object takes the path to that file and compiles it. After compilation, the template receives a reference to the ModelRoot and the output stream to which you should write the completed template. In that case, you are using the HttpResponse object's output stream as the destination for the finalized template. Once templates are compiled, you can process them in parallel, leading to a significant performance boost.

For anything other than the most trivial projects, FreeMarker provides a TemplateCache interface, implementations of which act as a repository for precompiled templates. The default implementation that FreeMarker provides, the FileTemplateCache, loads and compiles all the templates in a given directory and its subdirectories, storing them by the string representation of their path relative to a base directory (for example, "template_home/clients/orderlist"). Like all FreeMarker classes, you can extend or replace the FileTemplateCache class with custom code. An additional feature of TemplateCache objects is that they automatically reload templates when the template source file has changed. That relieves you of having to start and stop the servlet engine when you need to make changes to the HTML.

I mentioned earlier that FreeMarker supports an extremely lightweight, though robust, scripting language for templates. For instance, since you can think of SimpleList objects as similar to java.util.List objects, you'd expect (correctly) that there must be a way to iterate over them. One of the simplest scripting elements that FreeMarker provides is the list expression. The list expression works only on objects in the data model that implement TemplateListModel or TemplateScalarModel (in which case, it returns just the value of the scalar once and then returns). The syntax is:

<list data_model_element as index_variable>

where the data_model_element is the name of a list element in the ModelRoot (e.g., "orders") and index_variable is the name that you will use to refer to the currently indexed item. The name of the index variable is arbitrary. You can use the list expression to demonstrate how FreeMarker handily passes the MVC litmus test that JSP failed miserably. The FreeMarker code required to create the ordered list table would be:

<table>
   <list orders as thisOrder>
      <tr>
         <td>${thisOrder.date}</td>
         <td>${thisOrder.id}</td>
      <tr>
   </list>
</table>

Want to change that table to a list? Easy:

<ul>
   <list orders as thisOrder>
      <li>${thisOrder.id} was ordered on ${thisOrder.date}
   </list>
</ul>

Need to embed it in XML and pass it to an XSLT processor? Just as easy:

<customer>
   <customer_orders>
      <list orders as thisOrder>
         <order id="${thisOrder.id}" date="${thisOrder.date}"/>
      </list>
   </customer_orders>
</customer>

The view is completely unencumbered by the model, and vice versa. This is the power of the MVC pattern realized. As hinted above, FreeMarker tags and expressions are expanded inline, so you can easily embed them in the text of the template source regardless of the context. For example:

Click here to send me mail: <a href="mailto:${author.email}">${author.name}</a>

Could it get any easier? Actually, yes. For common view-related tasks that need to be shared across pages, FreeMarker supports the concept of a function. The syntax of functions is:

<function functionName(argName1, argName2...)>
    blah blah blah
</function>

Although there is nothing wrong with writing a function directly into the same file as the template that calls it, a more robust and reusable solution is to group functions in a separate file devoted to housing-related functions. When grouped like that, functions are accessible only when using FreeMarker's caching mechanism, as it is the only way for FreeMarker to locate files other than the current template (remember that the cache stores the templates by relative path). Another scripting element, the include "relative_path/name" expression, alerts the template processor to the request for an embedded template. If the given file contains functions, those functions are available for use by the current template. If the given file is another template, the contents of the template are expanded inline where the include is written. That expansion takes place much as attribute-related tags do, the difference being that include will expand the tags and includes of the embedded templates. This means that templates can be nested within one another, giving the page author the power to create modularized source files that can be plugged in at runtime. In fact, a very powerful idiom that introduces yet another scripting element, the <if>...<else>...</if> expression, is:

<if error>
<include "error.html">
<else>
<include "good.html">
</if>

This test will include the file "error.html" if the data model contains an element named error in it, otherwise "good.html" is included. Any tags or includes inside the "error.html" and "good.html" files will be expanded in turn. Assuming that the ordered list is reused across pages, you can modify the table-generating example to utilize the function and include keywords like so:

<include "javaworld_functions.txt">
<call show_as_table(orders) >

The function file ("javaworld_functions.txt") would look like this:

<function show_as_table(param1)>
   <table>
      <list param1 as thisOrder>
         <tr>
            <td>${thisOrder.date}</td>
            <td>${thisOrder.id}</td>
         <tr>
      </list>
   </table>
</function>

The finalized ClientOrderServlet (see source code) supports template processing either by direct template reference (where there is no expansion of embedded templates and no function resolution) or by template cache request (where embedded templates and functions are expanded and processed). The javaworld.template and javaworld_functions.txt files (see source code) contain tags calling out data generated by that servlet. To see the servlet in action, change lines 24 and 26 in the source code to reflect your operating system and development environment and install the servlet. Place the templates in the proper subdirectory of your Web server (i.e., the changes made in the previous step) and request it from your browser. You can toggle the conditional at line 23 to see how the two approaches differ. Feel free to mangle the servlet and template source to learn how FreeMarker will handle various other approaches. Remember, templates are reloaded if they change on disk, so don't bother restarting your servlet engine every time you make a change.

TemplateMethodModel: The JSP 1.1 killer

So far I've covered features of FreeMarker that resemble the JSP 1.0 paradigm, a passive model where the tags in the view are replaced with elements from the model. The JSP 1.1 custom tag architecture gives site authors the flexibility of passing data to a custom tag handler to help the handler do its job. For instance, the following code might be replaced with the text "Hello, Vinny" tricked out in a red font:

<jw:printhello color="red">Vinny</jw:printhello>

That flexibility translates into highly parameterized display logic. Unfortunately in the case of JSP, that flexibility comes at the cost of runtime performance, as it is based on Java Reflection. Additionally, because the attribute names are accessible only after they are registered in an intermediary configuration file, custom tags are potentially error-prone, not to mention difficult to debug. Lastly, as discussed earlier, it is still possible to violate the MVC pattern using JSP 1.1's architecture, which leads directly to maintenance nightmares. So you most certainly don't want to do that.

FreeMarker's TemplateMethodModel single-handedly usurps the muscle of JSP 1.1, while maintaining the split between model, view, and controller. The TemplateMethodModel interface defines one method, exec(). The prototype for that method is:

public TemplateModel exec(java.util.List arguments) throws TemplateModelException

You can see that exec() returns a TemplateModel object, which is the root of the FreeMarker hierarchy. The arguments are passed in sequentially as a java.util.List, letting any number of arguments be passed at runtime (since attributes map to accessors in JSP 1.1, there is no way to guarantee that any arbitrary attribute will be handled). To demonstrate, imagine a TemplateMethodModel object that executes one or more SQL statements passed to it. The TemplateMethodModel object accepts each SQL statement as a parameter and executes it against a database. Assuming the user known as "Vinny" has a "userid" of "333," the below HTML will print "Hello, Vinny!":

<HTML>
<BODY>
<H1>Hello, ${sql("select username from users where userid = 333")}!
</H1>
</BODY>
</HTML>

TemplateMethodModel objects are placed in the data model the same as any other model object:

public void doGet(HttpServletRequest req, HttpServletResponse res){
...
//the ModelRoot.
SimpleHash root = new SimpleHash();
root.put("sql", new SQLMethodModelImpl() );
...
}

The source code for SQLMethodModelImpl can be found in Resources. Take note of the amount of code required by FreeMarker relative to the amount of "do-it" code that accomplishes the task at hand. Outside of processing the passed parameters, the SQLMethodModelImpl code contains very few architecture-specific details. With custom tags you have to worry about the tag's start and end, and whether the JSP engine should skip the rest of the page or evaluate whatever comes after the tag. (Since when is doing nothing a feature?). This comparison should highlight the power of the TemplateMethodModel. FreeMarker's elegant design maximizes your time spent finding a solution to the problem set, saving you, if nothing else, hours spent reading JSP books to learn which methods handle which conditions.

A word about WebMacro

As you might expect, FreeMarker is not the only template processor available for Java. A popular competitor to FreeMarker is WebMacro, which supports tags similar to FreeMarker's in the template but very little in the way of scripting elements. Plugging WebMacro into your existing application is as simple as putting objects in a Hashtable, yet I believe WebMacro's approach combines the worst aspects of JSP and FreeMarker. Not only does it rely on Java Reflection to map attribute values in the template to methods on the server (a problem with JSP), but -- because it has very little in the way of a structured data model -- using WebMacro makes it nearly impossible to know what attributes are available to any given template. FreeMarker shares this flaw somewhat by requiring the page author to know the attribute names, say "order" or "date," used in the model when writing the view. Though some have argued that the lack of scripting support is actually a benefit, I think I've shown some very good examples of how scripting can save the page author loads of time and increase reuse without overcomplicating the development process. Even so, there are many reasons for preferring WebMacro to JSP, most of which are similar to the argument for FreeMarker.

Summary

This article has merely scratched the surface of what FreeMarker offers. I hope I've convinced you that FreeMarker represents a viable alternative to JSP. I strongly encourage you to download the FreeMarker distribution and begin coding with it yourself. One of the most gratifying elements of FreeMarker is that if you don't like something, even if it's the format of the ${...} expression, you can modify it. Once you experience FreeMarker's amazing powers of simplification, you'll never want to type another <%...%> tag again.

1 2 3 Page 2
Page 2 of 3