Velocity - An Introduction

by Thomas Paul

The Early Years of Servlet Development

I started working on my first servlet application shortly after the Java Servlet specification was released. It became readily apparent that Servlets were not designed to separate the Java developer from the HTML developer. In fact, since the HTML was inside the Java program, even trivial changes to HTML required the Java programmer to change code, recompile, and in many cases restart the Servlet engine.  We realized that this would make our applications difficult to maintain so we started looking for other options.  We eventually developed a system based on three major requirements:

1) The HTML must be kept in a separate file from the Servlet and can be easily changed without recompiling the Servlet.

2) The HTML must be easily edited with any of the many HTML tools available.

3) The Servlet must be able to plug dynamic content into the HTML easily.

If Velocity had been around then, we could have saved ourselves a lot of trouble because Velocity serves exactly this purpose.

The Advantages of Velocity

Velocity is a simple to use, open source web framework designed to be used as the view component of an MVC architecture.   You can use it as a replacement for JSP or alongside JSP.  It fits very well into more complex frameworks such as Struts or Turbine.

The main advantage of using Velocity over JSP is that Velocity is simple to use. The Velocity Template Language (VTL) is so constrained in its capabilities that it helps to enforce separation of business logic from the view.  You won't see any Java classes in a Velocity template (an HTML document with some Velocity placeholders).  In fact, Velocity is so simple to use that you can teach your HTML team how to use Velocity in just a few hours.   

Installing Velocity

Assuming you already have Tomcat installed, create a new web project called velocity and set up the appropriate WEB-INF directories. Under WEB-INF, in addition to lib and classes directories, create a directory called templates.

Velocity is very easy to install.You can get Velocity from the binary download page of the Apache Jakarta Project. (http://jakarta.apache.org/site/binindex.cgi) Download the zip or tar file. Inside you will find two jar files named, velocity-1.3.1.jar and velocity-dep-1.3.1.jar.  Extract these two files to the WEB-INF\lib directory of your velocity web project. 

That's it.  Velocity is now installed.  The Velocity jar files should always be added to the lib directory for each Velocity project.

A Basic Velocity Program 

Instead of going into a long, boring discussion of Velocity, let's look at a simple example of a Velocity program.We will start by assuming that we have created a web project called velocity in our Tomcat installation.

We will need to setup our web.xml file in our project's WEB-INF directory.  Here is what it may look like:

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

    <servlet>
        <servlet-name>HelloTest</servlet-name>
        <servlet-class>HelloTest</servlet-class>
        <init-param>
            <param-name>properties</param-name>
            <param-value>/WEB-INF/velocity.properties</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloTest</servlet-name>
        <url-pattern>/HelloTest</url-pattern>
    </servlet-mapping>

</web-app>
Sample 1. web.xml

This is a fairly typical web.xml file. We have added one parameter representing the properties file used by Velocity.  The properties file tells Velocity where to find the templates it will use and how to process them.  Here is a sample properties file:

resource.loader = file
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = c:/tomcat/webapps/velocity/WEB-INF/templates
file.resource.loader.cache = true
file.resource.loader.modificationCheckInterval = 2
Sample 2. velocity.properties

The resource.loader tells Velocity that we will be getting the templates from files.  Other options such as reading them from a jar file are also available. file.resource.loader.class tells Velocity the name of the class to use to read the files. file.resource.loader.path tells Velocity where to look for the templates.  file.resource.loader.cache controls whether the templates will be cached.  For performance reasons you will almost always wish to select true.  file.resource.loader.modificationCheckInterval tells Velocity to check to see if a template has been changed in two second intervals. Setting this option to 0 will turn checking off.

A Velocity template is simply an HTML document with some special placeholders that Velocity will recognize and replace with data you supply in your Java program.  For our simple test, we have a template named hello.vm.(It is standard practice to name Velocity templates with the extension of vm.)  Place the template in our WEB-INF/templates directory.

<html>
<body>
Hello $name
</body>
<html>
Sample 3. hello.vm

The $name in the document is a placeholder or reference in Velocity terminology.  When we run our Java program using this template $name will be replaced with the data we supply in our program.  Let's look at a Velocity Servlet.

import org.apache.velocity.Template;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import javax.servlet.http.*;

public class HelloTest extends VelocityServlet {

    public Template handleRequest( HttpServletRequest request,
                                   HttpServletResponse response,
                                   Context context ) {

        Template template = null;

        try {
            context.put("name", "Velocity Test");
            template = Velocity.getTemplate("hello.vm");
        } catch( Exception e ) {
          System.err.println("Exception caught: " + e.getMessage());
        }

        return template;
    }
}
Sample 4. HelloTest.java

The code is actually quite simple.Our Servlet extends VelocityServlet which is the base class for Servlets in Velocity.  We override the handleRequest() method which will be invoked when the Servlet is executed. Beside the normal HttpServletRequest and HttpServletResponse objects, Velocity also passes us a Context object. We will place the data we wish to use to fill the placeholders with into the Context object.In the example, the $name will be replaced by "Velocity Test".  We must return a Template object.  In our example, we are returning the template that we created above. 

Running this from http://localhost:8080/velocity/HelloTest will produce a screen that says "Hello Velocity Test".

Using a Java Class in a Template

That was simple so now let's look at something a little more complex.  Although we could fill our Context object with separate Strings for each placeholder, this isn't always required.  We could create a Java class with the appropriate get methods and pass that to our template.

Let's create a simple Customer class to use in our example.  The class has set and get methods that we can use to access the private Customer variables.

package com.javaranch.velocity;

public class Customer {

    String customerNumber;
    String name;
    String address;
    String city;
    String state;
    String zip;
    String phone;

    public String getCustomerNumber() {return customerNumber;}
    public void setCustomerNumber(String s) {customerNumber = s;}
    public String getName() {return name;}
    public void setName(String s) {name = s;}
    public String getAddress() {return address;}
    public void setAddress(String s) {address = s;}
    public String getCity() {return city;}
    public void setCity(String s) {city = s;}
    public String getState() {return state;}
    public void setState(String s) {state = s;}
    public String getZip() {return zip;}
    public void setZip(String s) {zip = s;}
    public String getPhone() {return phone;}
    public void setPhone(String s) {phone = s;}
}
Sample 5. Customer.java

Now let's create a test Servlet to create a Customer object and pass it to a template.

import org.apache.velocity.Template;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import javax.servlet.http.*;
import com.javaranch.velocity.Customer;

public class CustomerSearch extends VelocityServlet  {

    public Template handleRequest( HttpServletRequest request,
                                   HttpServletResponse response,
                                   Context context )  {

        Template template = null;
        Customer customer = getCustomer();

        try  {
            context.put("customer", customer);
            template = Velocity.getTemplate("customer.vm");
        }  catch( Exception e )  {
          System.err.println("Exception caught: " + e.getMessage());
        }

        return template;
    }

    private Customer getCustomer()  {
        Customer customer = new Customer();
        customer.setCustomerNumber("ABC123");
        customer.setName("Joe JavaRanch");
        customer.setAddress("123 Rancho Javo");
        customer.setCity("Bueno Ranch");
        customer.setState("CO");
        customer.setZip("63121");
        customer.setPhone("303-555-1212");
        return customer;
    }
}
Sample 6. CustomerSearch.java

The Servlet creates a Customer object and puts it into the Context object.  It then returns the customer template.  How does the customer template access the data from the Customer object?   The answer is that we use a simple dot notation.

<html>
<head><title>Customer Search Results - $customer.customerNumber</title></head>
<body bgcolor="#faf7f1">
    <h1>Customer information for customer number $customer.customerNumber</h1>
    <b>Name:</b> $customer.name<br>
    <b>Address:</b> $customer.address<br>
    <b>City:</b> $customer.city<br>
    <b>State:</b> $customer.state<br>
    <b>Zip:</b> $customer.zip<br>
    <b>Phone:</b> $customer.phone<br>
</body>
<html>
Sample 7. customer.vm

Each placeholder will invoke the get method from the Customer object.  For example, $customer.name will invoke the getName() method of the Customer object.

Do you notice what is missing from the template?   There is no mention of the Java class whatsoever.  Although we used $customer, that was only because "customer" was the name used when the Customer object was placed into the Context object.  Because this template doesn't know anything about a specific Java class, it will work with any Java class as long as it has the matching get methods. 

To test this, update your web.xml file and try running it. 

Using the Velocity Template Language

VTL is fairly simple but it does provide some very useful functionality.  There is, for example, an if-then-else structure available.  The most useful VTL command is the #foreach command which allows you to process through a Collection object.  This will allow you to display a list of data on your HTML page.   For a quick example, let's add a few invoices to the customer page.

First, we will need an invoice object.   I have created a very simple example excluding most of the information you would include in a real invoice class.  However, in addition to the normal get method for each variable, I have added a special display method that will format the data for output.  Here is the Invoice class:

package com.javaranch.velocity;

import java.util.*;
import java.text.*;

public class Invoice {

    Date orderDate;
    Date shipDate;
    float invoiceTotal;
    float shipTotal;
    float taxTotal;
    DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);

    public Date getOrderDate() {return orderDate;}
    public String getOrderDateDisplay() {return df.format(orderDate);}
    public void setOrderDate(Date s) {orderDate = s;}

    public Date getShipDate() {return shipDate;}
    public String getShipDateDisplay() {return df.format(shipDate);}
    public void setShipDate(Date s) {shipDate = s;}

    public float getInvoiceTotal() {return invoiceTotal;}
    public String getInvoiceTotalDisplay() {
        return NumberFormat.getCurrencyInstance().format(invoiceTotal);
    }
    public void setInvoiceTotal(float s) {invoiceTotal = s;}

    public float getShipTotal() {return shipTotal;}
    public String getShipTotalDisplay() {
        return NumberFormat.getCurrencyInstance().format(shipTotal);
    }
    public void setShipTotal(float s) {shipTotal = s;}

    public float getTaxTotal() {return taxTotal;}
    public String getTaxTotalDisplay() {
        return NumberFormat.getCurrencyInstance().format(taxTotal);
    }
    public void setTaxTotal(float s) {taxTotal = s;}
}
Sample 8. Invoice.java

We update the Customer class to include an ArrayList to hold invoices and add a couple of methods to add and get the Invoices:

package com.javaranch.velocity;

import java.util.*;

public class Customer2 {
    …
    ArrayList invoices = new ArrayList();
    …
    public void addInvoice(Invoice invoice) {invoices.add(invoice);}
    public ArrayList getInvoices() {return invoices;}
}
Sample 9. Customer2.java

The code above is added to the Customer object we created earlier.

Now we need a test Servlet to load the Customer2 object with Invoices and invoke the Velocity Template.

import org.apache.velocity.Template;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import javax.servlet.http.*;
import java.util.*;
import com.javaranch.velocity.*;

public class CustomerSearch2 extends VelocityServlet 
{
    
    public Template handleRequest( HttpServletRequest request,
                                   HttpServletResponse response,
                                   Context context )
    {
        
        Template template = null;
        Customer2 customer = getCustomer();
        
        try
        {
            context.put("customer", customer);
            template = Velocity.getTemplate("customer2.vm");
        }
        catch( Exception e )
        {
          System.err.println("Exception caught: " + e.getMessage());
        }
        
        return template;
    }
    
    private Customer2 getCustomer() {
        Customer2 customer = new Customer2();
        customer.setCustomerNumber("ABC123");
        customer.setName("Joe JavaRanch");
        customer.setAddress("123 Rancho Javo");
        customer.setCity("Bueno Ranch");
        customer.setState("CO");
        customer.setZip("63121");
        customer.setPhone("303-555-1212");
        addInvoices(customer);
        return customer;
    }
    
    private void addInvoices(Customer2 customer) {
        Invoice inv = null;
        Date[] datesOrder = {new GregorianCalendar(2004,01,21).getTime(),
            new GregorianCalendar(2003,11,18).getTime(), 
            new GregorianCalendar(2003,11,01).getTime()};
        Date[] datesShip = {new GregorianCalendar(2004,01,28).getTime(),
            new GregorianCalendar(2003,11,20).getTime(),
            new GregorianCalendar(2003,11,15).getTime()};
        float[] invTotals = {231.45f, 456.22f, 86.14f};
        float[] taxTotals = {18.21f, 43.18f, 3.11f};
        float[] shipTotals = {6.54f, 14.38f, 14.32f};
        for (int i=0; i<datesOrder.length; i++) {
            inv = new Invoice();
            inv.setOrderDate(datesOrder[i]);
            inv.setShipDate(datesShip[i]);
            inv.setInvoiceTotal(invTotals[i]);
            inv.setTaxTotal(taxTotals[i]);
            inv.setShipTotal(shipTotals[i]);
            customer.addInvoice(inv);
    }
}
Sample 10. CustomerSearch2.java

Now we need to create the new template.   We will add the #foreach command to process through the ArrayList.

<html>
<head>
    <title>Customer Search Results - $customer.customerNumber</title>
</head>
<body bgcolor="#faf7f1">
<h1>Customer information for customer number $customer.customerNumber</h1>
<b>Name:</b> $customer.name<br>
<b>Address:</b> $customer.address<br>
<b>City:</b> $customer.city<br>
<b>State:</b> $customer.state<br>
<b>Zip:</b> $customer.zip<br>
<b>Phone:</b> $customer.phone<br>
<p>
<h3>Recent Invoices:</h3>
<table border=1 cellspacing="0" cellpadding="5" >
<tr bgcolor="#FFFF00">
    <th>Order Date</th>
    <th>Ship Date</th>
    <th>Shipping Charge</th>
    <th>Tax</th>
    <th>Total Cost</th>
</tr>

#foreach ($invoice in $customer.invoices)
    <tr>
        <td>$invoice.orderDateDisplay</td>
        <td>$invoice.shipDateDisplay</td>
        <td>$invoice.shipTotalDisplay</td>
        <td>$invoice.taxTotalDisplay</td>
        <td>$invoice.invoiceTotalDisplay</td>
    </tr>
#end

</table>
</body>
<html>
Sample 11. customer2.vm

Let's look at this in detail.

#foreach will start a loop.  The loop will allow us to process all the data in the ArrayList.   $customer.invoices runs the getInvoices() method of the Customer object which returns an ArrayList.   Each entry in the ArrayList is extracted one at a time and named $invoice$invoice.orderDateDisplay will run the getOrderDateDisplay method of the Invoice object.  An iteration of the loop is terminated with the #end command.  When all the entries of the ArrayList have been processed, we will leave the loop.

Conclusions

Velocity is amazingly easy to use and to integrate into any Servlet application.  Simpler than JSPs, Velocity forces you to separate logic from content because VTL is so limited in its capabilities.  Since velocity uses placeholders in an HTML document, it is very easy to code your HTML using a fancy HTML editor.  The placeholders simply mark the place in your document that will get replaced with real data. 

Velocity can run standalone outside of a Servlet engine if you like.  You could, for example, use Velocity to read in a file and write out a new file with the placeholders updated.  In addition, Velocity has a suite of tools that can be used that provide additional functionality.  

Overall, Velocity is an extremely nice product.   Because it is so simple and concentrates on a single aspect of your web architecture it can be easily integrated into almost any web development project.