Take the fast track to text generation

Save time and frustration with template engines like Velocity, WebMacro, and Turbine

Most programs need to create some sort of text output, like email messages, HTML files, or console output. But because computers speak only in binary, programmers must make their software create intelligible text. In this article, I show you how using template engines can save you time when creating text output. You will learn the advantages of templates and find out how to create effective templates in different scenarios. Say goodbye to System.println()!

While programmers can easily write code to output text messages (after all, that's the first thing you learn in the Hello World example), often they're not the best people for composing messages in the first place. That is why we assign the job to marketing and public relations departments. But message writers unfortunately depend on programmers to complete even the most mundane tasks in terms of program output, which leads to misunderstandings and frustration for both sides.

Take a simple example: a Java program collects some client information from a data source and sends an account statement via email to each of your company's customers. Look at a sample program that accomplishes this task (the full examples are available for download from Resources):

        for (int i=0; i < customers.size(); i++)
        {
            Customer customer = (Customer)customers.get(i);
            
            StringBuffer message = new StringBuffer();
            message.append ("Dear Mr/Mrs ");
            message.append (customer.getLastName());
            message.append ("\n");
            message.append ("\n");
            message.append ("The total on your account is ");
            message.append (customer.getAccountTotal());
            message.append ("\n");
            message.append ("\n");
            message.append ("Regards");
            message.append ("\n");
            message.append ("Widgets and Gadgets Inc.");
            
            // Send Email
            mm.sendMail (customer.getFirstName(), customer.getEmail(), "Account", message.toString());
        }

The example above provides one of the worst ways to send a message. By embedding the message into the program source code, you make it virtually impossible for any nonprogrammer to edit the message without a programmer's help. You also make editing difficult for any programmer who does not know the code. Perhaps, because you foresee this detriment, you write the code below:

    static public final String STR_HELLO="Dear Mr/Mrs ";
    static public final String STR_MESSAGE="The total on your account is ";
    static public final String STR_BEY="Regards\nWidgets and Gadgets Inc.";

The code above doesn't ease the situation much, if at all. A nonprogrammer can hardly be expected to understand the meaning of static or final. Furthermore, code like this is not flexible enough to handle a change in message structure. You might, for example, be required to add more information from the data model to the email message, which would require you to change the email build code and possibly add more static final String objects.

Introduction to templates

Loading the message from a text file would solve some of the problems -- except for the provision of dynamic content, which is the most important aspect of the system. You need a way to insert dynamic content into a prewritten text message. If you use one of the text-based template engines available, it will complete all the hard work for you.

Template engines solve the problem of inserting dynamic content into a text message. Instead of embedding your message code into your program, you create a simple text file that contains the text content. A template engine parses that text file, called a text template, and any dynamic content is inserted with the help of simple template directives. The Jakarta Project's Velocity is my template engine of choice, but you can use one of the many other available engines. Velocity and WebMacro are probably the two most feature-rich and popular engines out there. Both are freely available under open source licenses.

Although I use Velocity in my examples, you should be able to easily port the examples to a different template engine by adopting the relevant engine's syntax.

Let's look at the email example I completed using Velocity. You need to download Velocity and add it to your classpath for the example to compile and run. You also need JavaMail if you want the email to work.

        for (int i=0; i<customers.size(); i++)
        {
            Customer customer = (Customer)customers.get(i);
            
            // Create a context and add all the objects
            VelocityContext context = new VelocityContext();
            context.put ("lastname",customer.getLastName());
            context.put ("total", new Double (customer.getAccountTotal()));
            context.put ("customer", customer );
            
            // Render the template for the context into a string
            StringWriter message = new StringWriter();
            template.merge(context, message);
            
            // Send Email
            mm.sendMail (customer.getFirstName(), customer.getEmail(), "Account", message.toString());
        }

First, you must understand the Java source code above. Instead of generating text as the first example did, the class above renders a text file called a Velocity template and emails the result to its recipient. A Velocity template can be any text file, but normally it contains some directives to insert dynamic content. The most interesting part of the Java code is the Velocity context. The Velocity context provides the link between your Java code and the Velocity text template, which another person could write. Any object added to the context is available to the template by the name given in the put() method's first parameter. To illustrate how that works, here is the template file:

Dear Mr/Mrs $lastname
The total of your account statement is $total
Regards
Widgets and Gadgets Inc.

When Velocity renders a template, it echoes all the text in the file except for the text that starts with $. A dollar sign indicates a location where a value from an object added to the context should be placed in the template's rendered output. Because I have context.put("name",customer.getName()) in the Java class, when the template renders, the customer's name replaces any call to $name. The output of the added object's toString() method replaces the Velocity variables (those beginning with $).

One of the most powerful and frequently used features of a template engine is its ability to discover object information using a built-in reflection engine. The engine allows you to retrieve the value of any public method or any object's data member added to the context with a convenient Java-like dot notation. The engine also provides another improvement: a shorthand for JavaBean properties. When using objects with JavaBean properties, you can omit the get part of the method and the trailing brackets. (For complete details check the documentation of your favorite template engine.) See the template below for an example. Because I added the Customer object to the context in the previous example's Java code, I can rewrite the template like this:

Dear Mr/Mrs $customer.LastName
The total of your account statement is $customer.AccountTotal
Regards
Widgets and Gadgets Inc.

Advanced template engines like Velocity and WebMacro can do more than merely replace variables. They also have built-in directives for comparison and iteration. (Even though comparison and iteration provide common ground between template engines, their syntaxes differ just enough for the templates not to be fully compatible. Keep that in mind when choosing an engine or migrating between offerings.)

Say, for instance, in December your boss wants to add a Christmas greeting to all your program's emails. You could just add this message to the template and remove it again later, but that would require you to show up for work on New Year's Day to remove the Christmas message.

A better idea is to tell the template when to display the message. To do that, you first add the current month to the Velocity context:

            int month = (new GregorianCalendar()).get(Calendar.MONTH);
            // add 1 to month because it is 0 based
            context.put ("month", new Integer(month+1) );

Now all you need to do is add the comparison to the template:

Dear Mr/Mrs $customer.LastName
The total of your account statement is $customer.AccountTotal
Regards
Widgets and Gadgets Inc.
#if ($month == 12)
    And a merry Christmas to you and your family.
#end

The #if directive is straightforward: a Boolean test determines whether to include the directive block's text in the template's rendered output. You can also perform more complex comparisons than simple equality, but these are mostly template-engine specific, so I will not cover them.

The iteration directive is just as easy as the #if directive. Template engines allow you to iterate through any implementation of the Java Collections Framework, including Array, List, and Iterator. For JDK 1.2 and higher, Java's Vector and ArrayList both implement the List interface, which makes them suited for use in template-engine iteration directives.

Instead of inserting the account total, suppose you would rather iterate through the account transactions and print a detailed report. The getTransactions() method (see the example code) of a Customer object returns a List object that contains zero or more Transaction objects. Because List is included as part of the Java Collections Framework, you can iterate through its contents using the #foreach directive. Don't worry about casting the objects -- the reflection engine does that for you. You can see how this works in the next example template:

Dear Mr/Mrs $customer.LastName
#foreach ($transaction in $customer.Transactions)
    $transaction.Description    $transaction.Amount
#end
The total of your account statement is $customer.AccountTotal
Regards
Widgets and Gadgets Inc.

The general form of a #foreach directive is #foreach <item> in <list>. It iterates through the list, placing each element in the <item> parameter and rendering the blocked content. The #foreach block repeats for each element in the list. In effect, you say to the template engine: "Repeat this block of text for each element in the list while putting each list element in the <item> variable once."

Model-View-Controller

Before proceeding to the next example, you should think about what you have learned so far. The biggest advantage to using a template engine is that you separate the code or program logic from the presentation or output. Doing so allows you to change the program logic without worrying about the message; similarly, you (or a member of the public relations staff) can rewrite the message without recompiling the program.

In effect, you separate the data model (the data classes), the controller (the mailer program), and the view (template) components of the system. This three-layered approach is called the Model-View-Controller (MVC) model. If you follow the MVC model, your code separates into three distinct layers, easing the entire software development process for everybody involved. (MVC has been around for some time; see Resources for more information.) A model used with a template engine can be any Java object(s), preferably one that uses the Java Collection Framework. Your controller needs to be only context aware, which is easy to add. Some of the object-relational mapping tools for relational databases work extremely well with template engines and help to ease Java Database Connectivity operations; the same applies for Enterprise JavaBeans.

Template engines concentrate more on the view part of MVC. The template language syntax is powerful enough to process all the necessary view functions, but is at the same time simple enough for nonprogrammers to use. The template syntax not only shields template designers from unnecessary programming complexity; it protects the system from code that is deliberately or accidentally harmful. Template writers cannot, for example, write code that results in a nonterminating loop or allocates huge amounts of memory. Don't underestimate the value of this safety net; most template writers don't have a programming background, and shielding them from programming complexities saves you time in the long run.

Many template users believe that the clear separation between controller and view components as well as their inherent safeguarding make template engines a viable alternative to other publishing systems, like JavaServer Pages (JSPs). It therefore comes as no surprise that the most common use of template engines is as an alternative to JSPs.

HTML

Because people tend to place such an emphasis on template engines as an alternative to JSPs, they sometimes forget that templates are also more broadly applicable. HTML Web content is by far the most popular use of template engines. I have also generated SQL, email, XML, and even Java source code with a template engine. I can only cover a couple of template applications here, but I include some extra examples in Resources.

For the HTML example, I will use the same model as I did in the email examples. The HTML screen is a hypothetical view in an organization's intranet that shows the details of customer accounts. The controller class in this case is a Java servlet, and the view consists of an HTML template. The code below illustrates the most interesting part of the servlet class. I wrote my servlet from scratch to make it more general, but template engines generally provide some servlet tools for you to ease the workload a bit.

            // Load the template
            Template template = Velocity.getTemplate("html.vm");
            
            // Create the context
            VelocityContext context = new VelocityContext();
            context.put ("customers",Customer.getCustomers());
            // Render the servlet and return the response             
            ServletOutputStream output = response.getOutputStream();
            Writer writer = new OutputStreamWriter (output);
            template.merge(context, writer);
            writer.flush();

You'll find nothing surprising here. I still just add the necessary object to the context and render the template. However, note that in the previous example I only added one Customer to the context, whereas I now add a list of Customer objects to the context. I can iterate through all Customers with a #foreach directive. Here is the accompanying HTML template:

<html>
    <body>
        <h1>Customer Report</h1>
        #foreach ($customer in $customers)
            <h2>$customer.FirstName $customer.LastName<h2>
            
            <table>
                #foreach ($transaction in $customer.Transactions)
                    <tr>
                        <td width="200">
                            $transaction.Date
                        </td>
                        <td width="150">
                            $transaction.Description
                        </td>
                        <td width="100">
                            $transaction.Amount
                        </td>
                    </tr>
                #end
                </tr>
                    <td></td>
                    <td></td>
                    <td><b>$customer.AccountTotal</b></td>
                <tr>
            </table>
            
        #end
    </body>
</html>

If you're planning a project that requires more than just a handful of HTML templates, consider one of the many template-based frameworks available. These frameworks give you the elegance of a template engine for generating HTML, while supplying you with a valuable set of tools, like database connection pooling and security, on which to build your application. Two common examples are Turbine and Melati, which are both compatible with Velocity and WebMacro. Both are open source and free.

Performance and configuration

In most programs, templates seem fast enough; but for high-volume Websites, you must look closely at performance. A template engine's most important performance feature is the caching of templates. Instead of loading the template from disk for each request, the template is parsed and stored in an optimal in-memory format. Template caching is usually disabled by default during development: traffic volume is low, and you want changes in templates to reflect immediately. After deployment, the templates will usually not change, and performance becomes a higher priority. Thus, at that point you should enable template caching.

In most template engines you can easily enable template caching by applying a setting or editing a Java properties file. In Velocity you can initiate the template with a Properties object. You can either create a Properties object manually, as I have done below, or load it from a properties file, which is probably better for an actual system.

            Properties props = new Properties();
            props.setProperty( "file.resource.loader.cache", "true" );
            props.setProperty( "file.resource.loader.modificationCheckInterval", "3600" );
            Velocity.init (props);

The file.resource.loader.cache property sets caching to true or false, while the file.resource.loader.modificationCheckInterval property sets the time in seconds for checking file changes. I cannot cover all the properties here in detail; consult your template engine's documentation for more information.

Free yourself from tedious programming

Advanced template engines are freely available and help you add template capabilities to almost any Java application. These engines provide easy-to-use tools for programmers and a simple template syntax for template writers so that developers can code programs with confidence.

You have learned that, by separating source code from presentation, templates can greatly ease the jobs of programmers and content creators alike. Templates liberate programmers from littering source code with messages and empower others to freely write content without interfering with program logic.

The clear separation that templates offer between logic and presentation also provides you with a better MVC design. This makes an attractive alternative to other publishing systems like JSPs, for it improves your applications' overall design without adding extra complexity.

About the author
Leon Messerschmidt, a Java developer and open source advocate, is currently working at Opticode Software. He has been involved in both commercial and open source Web application framework development for many years.
Learn more about this topic
  • Velocity and WebMacro are the two most popular template engines:
  • Read more about MVC and what it can do for your program design:
  • Template-based Web application frameworks:
  • Object-relational tools:
  • JavaWorld Resources:
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more