FreeMarker: An open alternative to JSP

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

FreeMarker, an alternative to JavaServer Pages, is a template-based, open source API that leverages the skills of Java developers and HTML authors in parallel. You can place FreeMarker tags in text files of any format and are not married to any server-side architecture or solution. FreeMarker is easily extensible, and you can quickly build a library of reusable custom objects that will live longer than the technology solutions that utilize them.

This article introduces the FreeMarker template engine and explains why it is viable and necessary alternative to JSP in Java-based model-view-controller implementations.

What's wrong with JSP?

JavaServer Pages is intended to be the glue that binds EJB and HTML code. Placed in the key boundary between data and presentation, JSP endeavors to be both intuitive enough for non-Java programmers and powerful enough for Java programmers to accomplish complex enterprise-level tasks on the server. The advent of JSP 1.1 and custom tag libraries is a welcome amendment to the original JSP design, yet JSP still falls short of its original purpose of separating presentation from data.

So what's wrong with JSP? It depends on the scope of the project. You cannot argue that one of JSP's strengths lies in integrating an existing codebase with an HTML-based presentation layer that is in need of a few dynamic elements. This integration often involves a Java developer hacking some JSP code into a previously authored HTML page.

In larger projects, time constraints often dictate that the HTML and Java be developed in parallel. Here's where JSP begins to show its faults. First, JSP relies too heavily on Java syntax. HTML coders seldom know Java syntax well enough to author JSP pages entirely by themselves. A typical solution is to have the Java developers "rope off" sections of HTML code with comments, to which they will later add JSP code. This approach leaves the door wide open for a complex form of feature-creep known as "Why doesn't it work the way it did when I originally wrote it?" Let's face it, HTML authors want their code touched about as much as Java programmers do, which is to say not at all. Additionally, it can be difficult to test the validity of the HTML depending upon how much the JSP dynamically generates it. This could leave the HTML authors sitting around waiting for the JSP to be inserted before they can even test their work.

You should also be pessimistic about JSP's long-range prospects. If you've ever tried to parse a typical JSP page with an XML parser, you know that there are elements of JSP (and not incidentally, also of HTML) that are not XML-compliant; for example, the <%...%> directive. This implies, of course, that current JSP tags are bound to HTML source files. This might not appear to be an obstacle now, and it won't be for a while. But some day in the future runtime optimizations will lead to XML output from the server and XSLT transformations on the client browser, and when that day comes all those JSP pages will be relegated to the seldom-touched recesses of their respective companies' source control systems. Additionally, to date, JSP hasn't been released under an open source license. Although it is unlikely that Sun will retract support for this technology, companies should be aware that they are building an extremely visible component of their business upon a technology whose modifications they have no control over.

For many, the most compelling argument against JSP is its halfhearted attempt at realizing the model-view-controller (MVC) design pattern. This pattern defines an architectural three-way split for an application, separating data elements (the model) from the data presentation (the view) from the manipulation of the data into the presentation (the controller). JSP makes a valiant attempt to live in harmony with this design pattern, but MVC proponents feel that requiring Java code (the controller) in the HTML (the view) is a violation of their beloved pattern. And they're right. JSP 1.1's custom tags eradicate some of this inconsistency, letting you build a library of tags that encapsulate logic and/or data and provide a JSP interface to them. The drawback is that custom tags are tiresome to configure and require a relative mountain of code to develop something you cannot use outside of JSP. Additionally, instead of relying on the Java language's strengths in inheritance, JSP custom tags rely on the Reflection API, which is a relatively inefficient technology. I say inefficient because instead of calling a method directly, Reflection lets you call a Reflection API method that will attempt to construct a reference to the desired method or class. This additional step comprises much of the inefficiency because of the time it takes to dynamically create the reference to the desired method or class.

To highlight some of JSP's shortcomings, an MVC litmus test might be useful. Here's the scenario: you'd like to display all the orders a particular customer has made over a certain period of time. This display should have the flexibility of being in an HTML table or in an HTML ordered list. According to strict MVC interpretation, this shouldn't be a difficult task; if presentation is truly independent of data, changing one should have no effect on the other. Unfortunately, in JSP you have two choices, neither of which is attractive. (Take note of where and how the table structure gets filled.)

  1. Using a JSP 1.0-like mechanism, you have a Vector (or other java.util.List-like object) containing order objects. This Vector is available to the JSP as a JavaBean:

    <table>
       <% 
          orderVector = customer.getOrders();
          for( int i=0; i<orderVector.size(); i++){
             Order o = orderVector.elementAt(i);
       %>
       <tr>
          <td><%= o.getDate() %></td>
          <td><%= o.getOrderID() %></td>
       </tr>
       <% } %>
    </table>
    
  2. Using a JSP 1.1-specific mechanism, you have a custom tag similar to <javaworld:orderlist/>. After configuring the tag library file and remembering to import the correct tag library at the top of the JSP file, you can start writing the Java code that looks like the following:

    ...
    BodyContent body = this.getBodyContent();
    JspWriter writer = body.getEnclosingWriter();
    writer.print("<table>");
          orderVector = customer.getOrders();
          for( int i=0; i<orderVector.size(); i++){
             Order o = orderVector.elementAt(i);
             writer.print("<tr>");
             writer.print("<td>" + o.getDate() + "</td>" );
             writer.print("<td>" + o.getOrderID() + "</td>" );
             writer.print("</tr>");
          }
          writer.print("</table>");
          ...
    

You can immediately see that neither of these two options is acceptable; each violates the MVC pattern. The first requires elements of the model in the view. Should the structure of the Order or Customer object change (which is, mind you, part of the model), changes would need to be made to the Java elements present in the view. That is not good, but it could be worse. In the ideal MVC environment, changes to one element should never affect another.

The second option requires elements of the view in the model. Should you need to change the HTML table (the view) to an unordered list, the custom tag would require modifications. That is even worse than the first option. In addition to violating good MVC practice, the second option forces a recompilation of the code, which implies regression testing, stopping the servers, a new or updated installation, and all the other little evils that ride on the coattails of changing deployed code. There has to be a better way.

FreeMarker to the rescue

In a perfect world, you'd expect your magic bullet to be an open source technology that's compatible with Java but not reliant on it, a strict MVC adherent, and independent of any source file type. Allow me to introduce FreeMarker, an open source project that meets all of the above standards. FreeMarker consists of a class library that provides template processing capabilities and an API that lets you extend or modify FreeMarker's behavior. Like JSP 1.1, FreeMarker's template processing engine replaces tags in the source file with dynamically generated data. Unlike JSP, FreeMarker tags are independent of the source-file format; they can just as easily be placed in an XML file, WML file, HTML file, or plain text file. FreeMarker is actually independent of servlets altogether, making it just as useful on the client as on the server. And unlike the JSP 1.1 XML-configured architecture, FreeMarker provides a lightweight framework that you can easily extend to build a powerful application without relying on runtime-inefficient technologies like Java Reflection to do the legwork. A unique feature of FreeMarker is its support for an embedded template scripting language. As you will see later in the article, this feature helps encapsulate commonly used presentation logic for use across pages.

The central concepts of FreeMarker are the template and the ModelRoot. In the FreeMarker world, a template is any text file that contains FreeMarker tags. These tags control the behavior of the FreeMarker template processor -- more on that later. The ModelRoot, as its name implies, is the root of the data model. The term "root" conjures images of a recursive treelike structure, and this is exactly what the FreeMarker's data model resembles. On a grand scale, objects conforming to the FreeMarker API are placed into the ModelRoot where they are extracted by the controller and placed into the view.

FreeMarker distinguishes among four categories of objects. The TemplateScalarModel, TemplateHashModel, TemplateListModel, and TemplateMethodModel interfaces describe what each of those objects should look like. FreeMarker kindly provides default implementations of all but the last of those interfaces, respectively known as SimpleScalar (scalars), SimpleHash (hashes), and SimpleList (lists). Each of these types should be familiar to you, as they resemble objects provided in the standard JDK distribution. This eases the learning curve for developers trained on these Java concepts. Scalars represent single-valued objects, such as a String or a boolean. Hashes are similar to the Hashtable object, an associative array of values retrieved by a unique key. Lists are similar to java.util.List objects, supporting read-only methods like next and hasNext methods, as well as other methods like rewind. (The SimpleList class contains additional methods to add objects to the list, but the interface doesn't require that). Finally, implementations of the TemplateMethodModel interface provide a mechanism for the presentation layer to call a method on the model layer, passing on the arguments it may need to complete the request. This is similar to JSP 1.1's custom tags except that it requires no special configuration. As you will see later, it is useful for, among other things, highly dynamic data extraction.

The elements of FreeMarker are orthogonal, letting you create complex data models that accurately represent the underlying object model. For instance, TemplateHashModel objects can contain any of the other object types, including instances of TemplateHashModel. The same goes for lists. Scalars are always the leaves of the ModelRoot tree as they cannot contain any other object type. The API requires that the object serving as the ModelRoot is an instance of an interface known as TemplateModelRoot, which SimpleHash conveniently implements. A hypothetical ModelRoot tree could have the structure shown in Figure 1:

Figure 1. The ModelRoot can contain any number of nested lists, hashes, and methods. Scalars are always the leaves of the tree.

The ModelRoot contains a TemplateListModel object, which in turn contains three TemplateHashModel objects. Each of those hashes contain key-value pairs, where the key is a java.lang.String object and the value is any object that implements the TemplateModel interface. This is the root interface that all FreeMarker objects must implement. In that case, the hashes contain TemplateScalarModel objects, which implement TemplateModel.

The face of FreeMarker

The MVC design pattern forces you to think of the view as an inherently different animal than the model and the controller. FreeMarker encourages that by providing a unique look to the presentation layer. At this layer, the syntax of FreeMarker tags is more reminiscent of a typical shell-scripting language than of Java code fragments. That proves to dramatically improve the learning curve for Webmasters and HTML authors. Tags in the source file call out attribute values in a familiar dot notation format. For instance, even without understanding the details of how FreeMarker works, it should be evident that ${user.preference.page_color} references the page_color attribute of the preference structure, which is contained in the user structure.

Conceptually, it is easiest to understand the job of the controller (FreeMarker's template processor) as being responsible for replacing the tags in the template with their corresponding elements from the ModelRoot. To illustrate, assume that you build a compound data model by adding objects to the ModelRoot as below. That code duplicates the JSP code from the beginning of the article with a noticeable FreeMarker flavor.

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.

Vincent DiBartolo has worked with Java since its days as mostly a client-side solution. He has worked on everything from JDK1.1 applets to batch applications using Java to server-side solutions like JSP/servlets and EJB. Always on the lookout for ways to improve the software development process, DiBartolo has tried his hand at most things Java and many things non-Java. Most recently he has been working extensively with XML-related technologies on the server and EJB containers such as Persistence PowerTier and BEA Weblogic. He received a BS degree in physics from the College of Mount St. Vincent and has worked in the defense, financial, and Internet industries.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more