Start up the Velocity Template Engine

Generate Web content with Java-based, open source Velocity

1 2 3 Page 2
Page 2 of 3

This is one way Velocity lets you decouple the view from your application's model-controller portion. Velocity's support for data access, with references, is a powerful and flexible facility. Your template references are only required to correspond to publicly accessible methods or be accessible through Velocity's property formalism.

Directives

Directives are VTL's other major part. Like references, they freely mix with other non-VTL content in templates. The directives in VTL are:

    #set()
    #if()
    #else
    #elseif()
    #end
    #foreach()
    #include()
    #parse()
    #macro()

The #set() directive lets you set a reference value within the template. This can replace an existing object in the context or establish a new one. Useful to the designer for presentation logic, #set() allows common mathematical operations on integers (+,-,/,*,%), as well as Boolean evaluations:

    #set( $startrow = 0)
    #set( $count = $current + 1 )
    #set( $isReady = ( $isOn && $isOpen) )

The #if() #else #elseif() #end set of directives provides the usual if/then/else functionality common in many programming and scripting languages. The #if() and #elseif() take an expression that can use the common logical operators &&, ||, !, ==, >, >=, <, <=, as well as a Boolean value:

    #if( $value > 5 )
      bigger
    #elseif( $value < 5 )
      smaller
    #else
      just right
    #end

The #foreach() looping directive has two arguments: the reference to which each value is assigned, and the value collection to loop over, plus a bit of "syntactical sugar" -- the word in:

    #foreach( $item in $itemList )
      My $item.
    #end

The collection types #foreach() supports are:

  • Object[]
  • java.util.Collection
  • java.util.Map (iterates over the mapped values)
  • java.util.Iterator
  • java.util.Enumeration

The #include() and #parse() directives are similar -- they both take a template or static resource name as an argument, include that template or resource in-place, and then render it to the output stream. The difference is that #include() includes the specified resource's content without any processing, and #parse() treats the specified resource as a template, processing all directives and references against the current context.

You can use these two directives to dynamically construct output. For example, in Web applications you can set up a skeleton for your pages, such as a set of top-level templates that may employ different navigational layouts or color/design schemes, and then #parse() and/or #include() the dynamic and static content elements at runtime based on user and application state:

    <table> 
      <tr><td> #parse( $header ) </td></tr>
      <tr><td> #parse( $body ) </td></tr>
      <tr><td> #parse( $footer ) </td></tr>
    </table>

The #macro() directive lets the designer create a parameterized bit of VTL code, called a Velocimacro, that can be invoked like a directive. Similar to a subroutine, this feature lets you create reusable pieces of VTL that contain explicit parameters for easier usage, readability, and maintenance. Velocimacro is a rich and powerful tool, and is best illustrated by example.

Velocimacro example

Suppose you want to encapsulate the VTL that designers use to make HTML <select> widgets. You would define a Velocimacro in regular VTL code:

  #macro( select $name $displayname $choicelist )
    <SELECT name=$name>
    <option value="">$displayname</option>
    #foreach( $choice in $choicelist )
      <option value=$choice>$choice</option>
    #end
    </select>
  #end

Then, anytime you need an HTML <select> widget, you could simply invoke the Velocimacro as a directive:

  Please choose:  #select( "size" "--SIZE--" $sizelist )

When rendered, Velocity dynamically expands Velocimacro using the arguments passed into the invocation.

Velocimacros have many interesting features, such as recursion ability, support for private local contexts, and even ability to be partitioned into private, template-specific namespaces. For more information, refer to the user and developer documentation available on the Velocity Website.

VTL miscellany

I should mention a few other VTL features. VTL lets you create integers, string literals, Boolean values, arrays of objects, and arrays of sequential integers in the template:

  #set( $myint = 5 )
  #set( $mystring = 'Hello There') 
  #set( $mybool = true )
  #set( $objarr = [ "a", $myint, $mystring, false ] )
  #set( $intrangearr = [1..10] )

VTL also has an alternative string literal form, using double quotes (") that interpolates reference values (plus directives and Velocimacros):

  #set( $foo = 'bar')
  #set( $interp = "The value is '$foo'")

The resulting value of $interp is the string The value is 'bar'.

You can find complete VTL documentation on the Velocity Website.

Same data, different templates

Now that you know about references and directives, let's continue with our Pet Store email example.

Suppose you switch to HTML for your email messages. With Velocity, you can easily do that just by changing the template. Your code can remain the same. Here is a different template that generates HTML from the same data:

  <HTML>
    <HEAD>
      <TITLE>Pet Store Sale!</TITLE>
    </HEAD>
    <BODY>
      <CENTER>
      <B>$petList.size() Pets on Sale!</B>
      <BR/> 
      We are proud to offer these fine pets
      at these amazing prices.  
      <BR/>
      This month only, choose from:
      #set( $count = 1 )  
      <TABLE>
        #foreach( $pet in $petList )
          <TR>
            <TD>$count)</TD>
            <TD>$pet.name</TD>
            <TD>$pet.price</TD>
          </TR>
          #set( $count = $count + 1 )
        #end
      </TABLE>
      <BR/>
      <I>Call Today!</I>
      </CENTER>
 
    </BODY>
  </HTML>

Using this template, your email body now looks like:

  <HTML>
    <HEAD>
      <TITLE>Pet Store Sale!</TITLE>
    </HEAD>
    <BODY>
      <CENTER>
      <B>3 Pets on Sale!</B>
      <BR/> 
      We are proud to offer these fine pets
      at these amazing prices.  
      <BR/>
      This month only, choose from:
      <TABLE>
          <TR>
            <TD>1)</TD>
            <TD>horse</TD>
            <TD>00</TD>
          </TR>
          <TR>
            <TD>2)</TD>
            <TD>dog</TD>
            <TD>9.99</TD>
          </TR>
          <TR>
            <TD>3)</TD>
            <TD>bear</TD>
            <TD>.99</TD>
          </TR>
      </TABLE>
      <BR/>
      <I>Call Today!</I>
      </CENTER>
 
    </BODY>
  </HTML>

This example demonstrates how Velocity lets you separate the presentation from the code and data; we used the same code and data with a different template.

XML template

As one final Pet Store example, let's use the same program to create an XML document detailing our sale. Again, we just change the template:

<?xml version="1.0"?>
<salelist>
#foreach( $pet in $petList )
  <pet>
    <name>$pet.name</name>
    <price>$pet.price</price>
  </pet>
#end
</salelist>

Using this template with the same program (maybe writing the output to a file), we get:

<?xml version="1.0"?>
<salelist>
  <pet>
    <name>horse</name>
    <price>00.00</price>
  </pet>
  <pet>
    <name>dog</name>
    <price>9.99</price>
  </pet>
  <pet>
    <name>bear</name>
    <price>.99</price>
  </pet>
</salelist>

Here is a trivial exercise for the reader: Make a template that outputs comma-separated data.

Programming with Velocity and servlets

Finally, let's use Velocity with servlets. The Velocity distribution includes a servlet base class, org.apache.velocity.servlet.VelocityServlet, that handles all Velocity and Servlet API housekeeping, letting you focus on your application. (Well, mostly -- nothing is ever that simple.) At the minimum, you must implement a method in which you put data into a supplied context, and return a template. The following class handles requests and renders templates using Velocity (sans real error-handling):

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
public class Sample extends VelocityServlet
{
    public Template handleRequest( HttpServletRequest request, 
         HttpServletResponse response, Context context )
    {        
        /* organize our data  */
        ArrayList list = new ArrayList();
        Map map = new HashMap();
        map.put("name", "horse");
        map.put("price", "00.00");
        list.add( map );
 
        map = new HashMap();
        map.put("name", "dog");
        map.put("price", "9.99");
        list.add( map );
        map = new HashMap();
        map.put("name", "bear");
        map.put("price", ".99");
        list.add( map );
        /* add that list to the context  */
        context.put("petList", list)
;        
        /* get the template */
        Template template = null;
      
        try
        {
            template =  getTemplate("petstoreemail.vm");
        }
        catch( Exception e )
        {
            System.out.println("Error " + e);
        }
        return template;
    }}

When configured and deployed properly, this would generate the same output as our HTML-generating Pet Store example, which the servlet engine would deliver to the client browser.

For a complete working example, including templates, deployment information, and additional configuration source code, see the servlet examples in the distribution provided by the Velocity Project.

Now it's your turn

Velocity embodies these great traits:

  • It's easy to use, both for designers and programmers
  • It separates your presentation formatting from your code
  • It lets you use your existing classes as they are
  • It's portable and works in any Java application
  • It's free and the source code is available under the Apache Software License, a business-friendly open source license

One of the interesting things about using Velocity is that the simplicity of the templating model encourages you to do things the "right way."

You can verify this claim. By now, you have the knowledge to get started with Velocity, and you should be convinced that the barrier to entry is fairly low. Examples are easy to write and can be either standalone programs or servlets. You can familiarize yourself with Velocity by downloading the distribution from the Jakarta Velocity Project Website. It has both standalone application and servlet examples, covering the basics and many advanced features as well. The documentation is good and a supportive community is there to help you -- feel free to join the mailing lists. You might not have use for Velocity right now, but you'll be surprised how often it solves problems in your everyday programming tasks.

Geir Magnusson Jr. is an independent consultant specializing in software development, system architecture, and real-time data delivery. He is a devoted contributor to the Velocity Project, and serves on the Jakarta Project's Project Management Committee. He is also a member of the Apache Software Foundation.
1 2 3 Page 2
Page 2 of 3