Velocity: Fast Track to Templating Velocity: Fast Track to Templating

by Erik Hatcher
12/16/2003

Velocity is a fast and easy-to-use Java-based templating engine. Velocity's speed, ease of use, and flexibility contribute to its use in a broad range of applications, including code generation, email templating, and web user-interface creation. A template is a parameterized, predesigned text format. A template engine processes a template and fills in the parameterized pieces with concrete data. The bulk of this article focuses on email templating with Velocity. The template, in this context, is an email body with special syntax used to indicate points within the email to insert specific data, such as a name, order number, or order details. This article first introduces Velocity with a simple, easy-to-run example, then briefly covers the templating syntax, and ends with a full-featured and detailed look at Velocity in action for templating automated emails.

Owner's Manual

Before we delve into the details of Velocity's inner workings and syntax, let's get a straightforward example working to make sure that all of the pieces are in place and we have a framework in which to experiment.

Follow these four steps:

  1. Download Velocity's binary distribution (version 1.3.1 was used for this article).

  2. Save StartYourEngines.java (see the listing below) to your local file system.

  3. From the command line, compile:

    javac -classpath <path to velocity -dep JAR> StartYourEngines.java

    Velocity ships with two JAR files, one with -dep in its name. The -dep JAR file includes all third-party dependencies needed to run Velocity, such as several of the Jakarta Commons APIs. The velocity-dep-1.3.1.jar file is used for all code in this article.

  4. Now run it:

    java -classpath <path to velocity -dep JAR>:. StartYourEngines uno dos tres

    You should see this output:

    args =  uno  dos  tres

You can see in this code listing that the StartYourEngines program places the command-line arguments array into a Velocity context. The template is an embedded string that iterates over each item in the array and outputs it. In the next section we will define Velocity's terminology.

StartYourEngines.java

import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;
import java.io.StringWriter;

public class StartYourEngines {
  public static void main(String[] args) throws Exception {

    VelocityContext context = new VelocityContext();
    context.put("args", args);

    String template = "args = #foreach ($arg in $args) $arg #end";

    StringWriter writer = new StringWriter();

    Velocity.init();
    Velocity.evaluate(context,
                      writer,
                      "LOG",  // used for logging
                      template);

    System.out.println(writer.getBuffer());
  }
}

Under the Hood

There are two key concepts to understand with Velocity: the context and the template. The following diagram illustrates these concepts, along with the merging of a context and a template to generate output.

The data placed in the Velocity context is accessible to the template. For example, processing a template containing Dear ${person.firstName} would replace ${person.firstName} with the value of the firstName property of the object named person.

Velocity in Action

Fill 'Er Up: The Context

Velocity's context is a container of Java objects, each with a unique name to reference it from a template. There are no restrictions on what types of objects can be placed in a context. Collections, arrays, and maps are all easily dealt with by Velocity; the args object in the StartYourEngines demo is an example of placing an array in the context. JavaBeans and collections of JavaBeans are the most common object types used to push data into a template.

Following the Roadmap: The Template

A Velocity template is text that serves as a model for output. Templates contain a combination of references to context data, Velocity Template Language (VTL) directives, and static text. Static text is passed through, as is, into the generated output. References and VTL are processed by the Velocity engine.

The StartYourEngines demo template uses a combination of static text (args =), VTL (#foreach and #end), and references ($arg and $args). The following two sections cover references and VTL in greater detail.

References

Objects in the Velocity context are accessed using references from within a template. References begin with a dollar sign. The name following the dollar sign refers to a name of an object within the context. Since you may want to nestle a reference with static text following it, formal notation (surrounding the complete reference with curly brackets) may be used to keep things clear and unambiguous. For example, to put the fields name and number together, separated by a dash, ${name}-${number} is used, with curly brackets around the references. Had the brackets not been used, $name would not be expanded, as the processing would look for a context object named name-.

If a context object does not exist for a named reference, the reference itself ($whatever) is left as is in the output, unless quiet reference notation is used. References with an exclamation point after the dollar sign, such as $!name and $!{name}, are replaced with an empty string when the reference does not exist within the context.

So far, we've seen simplistic references that refer directly to objects in the context. These are called variable references. There are two types of references. Nested properties of context objects are referenced using property references. Method references invoke methods on objects within the context.

Property References

A dotted syntax is used to access properties of context objects. The expression ${person.name} will output the name property of the context object named person. The person object could be a simple JavaBean such as this, and the getName() method will be called:

public class Person {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

Or person could be a java.util.Map with an entry named name. The following test case illustrates a Map being used identically to a JavaBean in a template.

public void testMap() throws Exception {
  Map person = new HashMap();
  person.put("name", "Duke");

  context.put("person", person);

  Velocity.evaluate(context, writer, "TEST", "${person.name}");
  assertEquals("Duke", writer.getBuffer().toString());
}
Method References

Velocity is a Pull-MVC framework, referring to the templates' capability of pulling information that was not explicitly pushed into the context. Variable and property references are using a push model. The pull action comes from method references. Methods on the context objects are called in the same manner that they are in Java, using a parenthetical expression with any required arguments. Method calls are primarily used for data formatting, although they can certainly be leveraged for other purposes, such as computations.

It is common to see utility objects with useful formatting or computation methods placed into the context. This is such a common practice that these objects are known as tools. The Velocity distribution ships with VelocityFormatter, a class with some date, array, and other formatting methods. The testFormatter test demonstrates using this tool and its formatShortDate method. Note that a property reference is used as a parameter to formatShortDate, which requires a java.util.Date argument. Formatting tools allow developers to put rich objects into the context and defer formatting decisions to the template writers, where it belongs.

public void testFormatter() throws Exception {
  Date today = new Date();

  context.put("formatter", new VelocityFormatter(context));
  context.put("today", today);

  Velocity.evaluate(context, writer, "TEST", "today is $formatter.formatShortDate($today)");

  DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT);
  String expected = "today is " + format.format(today);
  assertEquals(expected, writer.getBuffer().toString());

}

Velocity Template Language

In the initial example, the #foreach directive was used to iterate over an array. There are only a handful of other built-in directives that compose the Velocity Template Language (VTL). All of these directives are listed in the following table.

Directive Syntax Purpose
#foreach
#foreach ($item in $collection)
  item is $item
#end
Iterates over a collection, array, or map.
#if / #else / #elseif
#if ($order.total == 0)
  No charge
#end
Conditional. Null can be checked for using #if ($obj) obj not null #end syntax.
#parse #parse("header.vm") Loads, parses, and incorporates the specified template into the generated output.
#macro
#macro(currency $amount)${formatter.currency($amount)}#end
Defines a new directive and any required parameters. The result is interpreted when used later in the template. The example macro would be used as #currency($item.cost).
#include
#include("disclaimer.txt")
Includes the specified file, as is, into the generated output.
#set
#set ($customer = ${order.customer})
Assigns a value to a context object. If the context object does not exist, it is added; otherwise, it is replaced.
#stop
#if ($debug) #stop #end
Stops template processing. This is usually used for debugging purposes.

This is the entire set of built-in directives. New directives may be defined using #macro. Velocity's User Guide does a great job of giving detailed information on VTL -- please refer to it for more explanation and usage examples.

Shifting Into Overdrive: Email Templating Example

It is almost guaranteed that the systems you work on need to send automated emails to the users, either as receipts for online purchases or for system-event notifications. These emails are very likely dynamic, such as the case of an email receipt listing the items purchased, their costs, and an order number. There are many technical solutions to this problem, including using JSP or tokenized regular-expression substitutions. Velocity, of course, is the recommended solution here, as it provides all of the needed flexibility and a straightforward template language allowing even your end users customization capabilities.

This example is going to pull out all of the stops illustrating the majority of Velocity's capabilities. Our user story is this:

Implement a Java method that accepts an Order and sends a receipt email to the customer. The email format must allow easy runtime customization.

From a top-down approach, our method interface is:

public void sendReceipt(Order order) throws Exception

An Order is a Java object encapsulating a Customer, order line items (a collection of Items), order number generation, and a method to compute the order total.

import java.util.List;
import java.util.Iterator;
import java.util.Date;

public class Order {
  private Customer customer;
  private List lineItems;
  private String orderNumber;

  public Order (Customer customer, List lineItems) {
    this.customer = customer;
    this.lineItems = lineItems;

    // for example purposes, "generate" a simple order number.
    orderNumber = customer.getId() + "-" + new Date().getTime();
  }

  public Customer getCustomer() {
    return customer;
  }

  public List getLineItems() {
    return lineItems;
  }

  public String getOrderNumber() {
    return orderNumber;
  }

  public float total() {
    float total = 0;
    for (Iterator iterator = lineItems.iterator(); iterator.hasNext();) {
      Item item = (Item) iterator.next();
      total += item.getCost();
    }
    return total;
  }
}

Customer and Item are fairly typical JavaBeans, shown below.

public class Customer {
  private String firstName;
  private String lastName;
  private String email;

  public Customer (String firstName, String lastName, String email) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getEmail() {
    return email;
  }

  /**
   * For demonstration purposes, an id is a concatenation of
   * first and last initials
   */
  public String getId() {
    return "" + firstName.charAt(0) + lastName.charAt(0);
  }
}


public class Item {
  private String description;
  private float cost;

  public Item(String description, float cost) {
    this.description = description;
    this.cost = cost;
  }

  public String getDescription() {
    return description;
  }

  public float getCost() {
    return cost;
  }
}

Now that we've got the underlying details out of the way, the Emailer class usage becomes:

  /**
   * Example usage of Emailer functionality
   */
  public static void main(String[] args) throws Exception {
    Emailer emailer = new Emailer();

    ArrayList lineItems = new ArrayList();
    lineItems.add(new Item("Java Development with Ant", 44.95f));
    lineItems.add(new Item("Lucene in Action", 41.37f));

    Customer customer = new Customer("Duke", "Jahvah", "duke@java.net");

    emailer.sendReceipt(new Order(customer, lineItems));
  }

Still no view of Velocity -- this is an intentional design decision, to keep things decoupled and cohesive. The Emailer class itself fully encapsulates the use of the Velocity API. Velocity is transparent to developers using Emailer, except for the need to create a corresponding template. Before we proceed deeper into the code, we need to first analyze the end goal, an actual email. Here is an example email:

Duke,

Thank you for your purchase.
Your order number is DJ-1070292605890.

Description                    Cost
Java Development with Ant      $44.95
Lucene in Action               $41.37

Total                          $86.32

Visit us again at http://java.net!

Seeing an actual email body gives us some insight into some implementation details. First, note some formatting concerns. The description is output in a fixed-width style. Cost and total are formatted as currency. If our system is designed to service multiple stores, store information such as the URL in the last line perhaps should be provided dynamically into the context rather than being fixed text in the template. One final foreshadowing of the issues to address: what about the subject of the email? Shouldn't this be customizable using the same type of templating?

Continuing the outside-in zoom into Emailer, we see the Emailer constructor and the sendReceipt method.

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.util.ArrayList;
import java.util.Properties;
import java.util.HashMap;
import java.io.StringWriter;

public class Emailer {

  VelocityEngine engine = new VelocityEngine();

  public Emailer() throws Exception {
    configure(engine);
  }

  /**
   * "Sends" (actually writes to System.out for demonstration
   * purposes) a receipt e-mail for the specified order.
   */
  public void sendReceipt(Order order) throws Exception {
    Template template = engine.getTemplate("email.vm");

    VelocityContext context = createContext();
    context.put("order", order);

    StringWriter writer = new StringWriter();
    template.merge(context, writer);
    writer.close();

    System.out.println("To: " + order.getCustomer().getEmail());
    System.out.println("Subject: " + context.get("subject"));
    System.out.println(writer.getBuffer());
  }

  // ...configure and createContext coming soon...
}

A few new tricks are introduced here. First, VelocityEngine is used, rather than the singleton Velocity seen in the earlier examples. VelocityEngine is an instance-based way to invoke the templating merge, keeping configuration separate from other instances, whereas the singleton does not. The template is external to our code (more on this later). Also of note is the call to context.get("subject"). The context is not a one-way "push," thus allowing the template to inject items back into it. In this case, our template pushes an email "subject" string into the context and the sendReceipt method retrieves it. It is handy to keep the subject and body of an email close together, and placing them both in the same template allows for customization of both the subject and body in one spot.

Configuration of Resource Loaders

The trickiest thing when working with Velocity is configuration. Several configuration options are available with Velocity. For the Emailer example, Velocity needs to know how to find the template. Velocity has a resource loader abstraction with built-in loaders to retrieve templates from the file system, the classpath, or a data source. Custom resource loaders could be written to retrieve templates in a way custom to your architecture if needed. Our email.vm (.vm for Velocity macro) template is not hardcoded into our source code. Rather, it lives as a file on the classpath. Velocity can be configured either through the API, or through a velocity.properties file. I prefer controlling it through the API to avoid the issue of where to put the velocity.properties file. Most of Velocity's documentation, however, will show configuration using the properties-file syntax. Configuring through the API is a simple translation; here is the properties file equivalent of the configure method:

resource.loader=classpath
classpath.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader

Using some provided constants, the same configuration is achieved using the API. The term classpath in the configuration examples is an arbitrary name used to tie the classname (and potentially other configuration) to a specific loader.

  /**
   * Configures the engine to use classpath to find templates
   */
  private void configure(VelocityEngine engine) throws Exception {
    Properties props = new Properties();
    props.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
    props.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
                      ClasspathResourceLoader.class.getName());
    engine.init(props);
  }

Refer to the Velocity Developer's Guide for more details on configuring resource loaders and other parameters.

Emailer Context

To put the final touches on our Emailer class, our context is created with more than just the Order object. In order to give our template control over formatting (rather than forcing our Java code to deal with it less flexibly), a formatter tool is placed in the context. General store information is injected into the context as a Map.

  /**
   * Creates a Velocity context and adds a formatter tool
   * and store information.
   */
  private VelocityContext createContext() {
    VelocityContext context = new VelocityContext();
    context.put("formatter", new Formatter());

    HashMap store = new HashMap();
    store.put("name", "java.net Bookstore");
    store.put("url", "http://java.net");

    context.put("store", store);
    return context;
  }

Formatter is the final Java code to show before we get to the template. Two formatting functions are needed: padding or truncating a string to fixed width, and converting a float amount into a pleasant, locale-dependent currency display.

import java.text.Format;
import java.text.NumberFormat;

public class Formatter {
  public String currency(float amount) {
    Format formatter = NumberFormat.getCurrencyInstance();
    return formatter.format(new Float(amount));
  }

  public String pad(String string, int width) {
    if (string.length() >= width) {
      return string.substring(0, width);
    }

    StringBuffer output = new StringBuffer(string);
    for (int i=0; i < (width - string.length()); i++) {
      output.append(' ');
    }

    return output.toString();
  }

}
Nearing the Finish Line: The Email Template

Even though we're on our last lap, keep your seat belts fastened. The email.vm template utilizes several VTL directives, including the cool #macro. A detailed analysis of the template follows.

 1. #set ($customer = ${order.customer})
 2. #macro(currency $amount)${formatter.currency($amount)}#end
 3. #macro(pad $string)${formatter.pad($string, 30)}#end
 4. #macro(description $item)#pad($item.description)#end
 5.
 6. ${customer.firstName},
 7.
 8. Thank you for your purchase.
 9. Your order number is ${order.orderNumber}.
10.
11. #pad("Description") Cost
12. #foreach ($item in ${order.lineItems})
13. #description($item) #currency(${item.cost})
14.
15. #end
16.
17. #pad("Total") #currency($order.total())
18.
19.
20. Visit us again at ${store.url}!
21.
22. #set ($subject="${store.name} receipt")

The line numbers on the left are not part of the original template, but rather for discussion purposes. Lines 6 through 20 make up the email body. The only objects in the Velocity context are order, store, and formatter.

Line 6 refers to customer.firstName. The customer object was created on Line 1 using #set. It is merely there for convenience, simplifying access to the customer object that is nested within the order object. First name could also be displayed using ${order.customer.firstName}.

Column headings are generated on Line 11. To keep things aligned, the #pad macro is defined on Line 3, wrapping the Formatter.pad method invocation. This keeps the width in one place within the template. The item descriptions are also padded, but they go through a #description wrapper around #pad that is Item aware. Alternatively, #pad could have been used on Line 13, like #pad($item.description).

Currency is formatted on both Lines 13 and 17. On Line 17, the Order.total() method is invoked.

The URL to the store, rather than being fixed in the template, comes from the store context object. This context object is a Map that contains a url-named entry. Pleasantly, the template deals with object properties and map entries identically, so it is possible to switch the underlying implementation of the context object without affecting the template.

And finally, Line 22 performs the trick previously mentioned, injecting a subject object into the context, which is retrieved after the merge in Emailer.sendReceipt.

White space is always an issue with templating engines. Velocity does some intelligent things to collapse white space, but experience shows that experimentation is needed to tweak a template into generating the exact desired output. The #macro and #set directives on Lines 1-4 and 22 do not directly cause any output during the merge. Notice that the #macro definitions are completely collapsed to avoid them generating undesirable spaces when used later in the template. The blank Line 14 is needed to put each item on a separate line.

Lane Ends, Merge Right

This has been a speedy look at Velocity, yet all of the major pieces have been covered. Adding Velocity to your technical toolkit is highly recommended. A general-purpose templating engine comes in handy in many aspects of development, and Velocity is the best one for the Java language. This look at Velocity covered one primary use, generating automated emails; there are many other uses, which can be extrapolated from the examples provided here. For example, generating HTML output from a web application using Velocity merely involves morphing the code shown in Emailer into a servlet (but refer to Resources before doing so, as several such implementations are already available). More details on Velocity's syntax, API, and configuration were intentionally omitted from this article, since these are covered in Velocity's excellent provided documentation.

Resources

Erik Hatcher is the co-author of the premiere book on Ant, Java Development with Ant (published by Manning), and is co-author of "Lucene in Action".


 Feed java.net RSS Feeds