ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Migrating to Velocity Migrating to Velocity

by Jason R. Briggs
04/14/2004

There are a variety of choices when it comes to producing dynamic web content. Typically, most projects will go with the accepted or de facto standards, so chances are, if the platform choice for your project is Java, you're likely to be using JSPs. This makes sense for a number of reasons: new staff are more likely to have experience with a standard; a number of web servers support JSPs, thus giving you a wide choice of web platforms; there is plenty of documentation available since people have usually "done it before"; and so on. However, sometimes using the standard isn't the best choice. Perhaps you need or want more direct control over the code producing your content; perhaps you have performance issues with JSPs; or maybe it's just that a new technical direction within your company is pushing away from that paradigm. Velocity is a simple, yet powerful, templating engine that can be used to generate dynamic content as an alternative to the incumbent standard. In some cases it provides a performance advantage over JSPs, and it certainly forces a web developer to think differently about separating content from code.

The one major downside is that if you already have a JSP-based project, or have a less experienced team that is already skilled in JSPs, switching to Velocity presents its own set of challenges. In this article, I try to remove some of these obstacles, using some simple Velocity extensions that I hope will reduce some of the pain you might experience in migration.

A Bit of Background

Almost two years ago I inherited an average-size, servlet-based system that had been (badly) written by a Northern European company (proving that not everything that comes out of Northern Europe is of Linux quality). The software used a very basic template engine, but there was also a lot of HTML embedded in the code as well. In fact, there was a rather long list of poor design decisions, and nasty pieces of ineptly managed code (including the huge-method-that-does-everything style of development) just waiting to ensnare the unwitting developer in a maintenance nightmare of twisting spaghetti code.

Considering the limitations of the system as it was then, and the requirements my company had for a more fully featured suite, I made the decision to scrap 99 percent of the code, and start fresh, designing a new J2EE system from scratch. The new system has been running for almost a year now, with fairly few hiccups. It is an order of magnitude more maintainable, and, with one exception, has been enjoyable to work on.

Of late, and despite the fact it may be considered an unfashionable viewpoint, I have found myself becoming dissatisfied with my original decision to go with JSPs for the web and WAP interfaces. The original design endeavored to be as close to the MVC pattern as possible, which isn't all that simple considering how tempting it is to embed Java code in your web pages. The task was made easier by the use of the JSTL (standard tag library), but inevitably, you find there is something you just can't do with JSTL, so you write your own taglib to do the task. And then there are the objects you need to use in the page that, through some quirk of fate, simply will not work properly with JSTL unless you wrap them with a bit of Java code. Or, more commonly, there are methods that have to be called and, due to time constraints during that part of development, were just easier to call directly with Java. Suffice it to say that the final result, while perfectly functional, is less elegant than one might have hoped for at the beginning of development.

Enter the Challenger

Re-evaluation of the web part of our technology set has recently become more important due to an issue with our servlet container and the way it handles JSPs under heavy load (the infamous "Too Many Open Files" problem), and after examining some of the myriad choices available, I settled on Velocity due to a couple of important considerations:

  1. Open Source — whether or not it's an important concern for others, I run a Linux shop, so open source software is usually our first port of call.

  2. Performance — my initial tests indicated that for simpler pages, Velocity outperforms JSPs by between 35 percent ad 45 percent. For more complicated pages, the difference starts to diminish (and after you add all the other factors that go into an average dynamic web page, it can sometimes drop to only a 5 percent difference), but the improvement is there for the taking.

  3. Elegance — Velocity's syntax is simple and effective. It's also pretty straightforward to add new commands (or directives as they're called) without violating that convention of simplicity.

I also happen to agree with the Velocity point of view that suggests if you hand a JSP/JSTL page over to a designer they will have a much harder time than if you hand them a Velocity template (the disadvantage of the JSP/JSTL approach being that if you're already looking at XHTML, having XML tags for the dynamic content doesn't make life any easier for a non-technical web designer).

How It Works

In Velocity, for each page (or if you prefer, for each group of pages), you write a servlet. Your servlet marshals all the data you need for that page, places it in a context, and finally, you tell Velocity to render that context using a template. The template contains your HTML markup and Velocity directives — no Java code.

This is, of course, why it is so effective; by forcing you to severely limit the code in the template, you simplify your pages — in effect enabling you to hand them off to a web designer in a way that I don't personally believe you can do as well with JSPs.

Contrast this with our JSPs: we use JavaBeans to hide our Java code away as much as possible. We then include these beans in our pages using the JSP tag useBean, and set properties against those beans before we retrieve the data using JSTL tags:

<jsp:useBean id="comp" scope="session" 
    class="com.lateralnz.comp.view.CompDefBean" />
<jsp:setProperty name="comp" 
    property="someprop" value="someval" />

...
...

<c:forEach var="comp" items="${comps.iterator}">
  <tr>
    <td class="list">
      <c:out value="${comp.compCode}" />
    </td>
...

As you can imagine, a straight conversion from JSP to Velocity represents a significant amount of footwork, not to mention the learning curve involved if you are leading a relatively inexperienced team. Time is always an issue with any project, and while our system isn't huge by any stretch of the imagination, we still didn't have the resources to convert a few hundred JSPs to an alternate technology, especially when that technology requires you to work with significantly different methods. Accordingly, I made the decision to do a gradual migration to Velocity, taking a more measured approach.

Adding the Missing JSP Functionality

Step one is to allow Velocity templates to be called in the same way as a JSP. To start with I created a basic servlet (VMServlet) and then set up a mapping with the url-pattern "*.vm":

<servlet-mapping>
    <servlet-name>vm</servlet-name>
    <url-pattern>*.vm</url-pattern>
</servlet-mapping>

This means that http://mydomain.com/mypath/test.vm will be passed through as a standard servlet call to VMServlet. The main method of VMServlet, processRequest, is called by the standard servlet methods (doGet, doPost), and performs the operations required to add a number of implicit objects to Velocity's context, load the template, and produce the actual output.

protected void processRequest(
    HttpServletRequest request, 
    HttpServletResponse response) 
  throws ServletException, IOException {

Here the template is loaded using standard Velocity methods (templates are cached by the engine):

  String templateName = request.getServletPath();
  Template tmp = ve.getTemplate(templateName);
  VelocityContext ctx = new VelocityContext();

The web context path, session, jsessionid, and servlet request all go into the context (these are the most common objects we use in our JSPs):

  HttpSession session = request.getSession(true);      
  ctx.put("contextPath", 
          request.getContextPath());
  ctx.put("session", session);
  ctx.put("jsessionid", JSESSIONID_STR + 
          session.getId());
  ctx.put("request", request);

The final step is to render the output, using a PrintWriter, the Velocity Context, and the template:

  response.setContentType(contentType);
  PrintWriter out = response.getWriter();
  tmp.merge(ctx, out);

Duplicating a JSP

Taking a look at a basic JSP, there are still some features that are required in order to facilitate our migration.

In the example below, we set the content type, instantiate a JavaBean (a Date object), include a couple of JSP "fragment" files, and use the JSTL taglib fmt in order to display a heading and message from a ResourceBundle. The last line calls the getTime method on the Date (this could just as easily be a call to the JSTL c:out tag to get the same data).

<%@ page contentType="text/html; charset=UTF-8" %>
<fmt:setBundle basename="com.lateralnz.messages" var="sysm" />
<jsp:useBean id="test" scope="session" class="java.util.Date" />
<html xmlns="http://www.w3.org/1999/xhtml">

<%@ include file="htmlhead.jsf" %>

<body>
<%@ include file="header.jsf" %>

<h1><fmt:message key="app.welcome_title" bundle="${sysm}" /></h1>

<p><fmt:message key="app.welcome_text" bundle="${sysm}" /></p>

<p><%= test.getTime() %></p>

</div>
</body>
</html>

Velocity provides directives to duplicate some, but not all, of this JSP. In a typical Velocity installation, a servlet would place the Date and ResourceBundle objects into the context so they could be called from within the template. Step two then, is to get around this necessity (ignoring for a moment that doing so goes back somewhat on that MVC goal I mentioned earlier). I create my own directive called #usebean (see UseBeanDirective.java for the code), which is passed a name, the name of the class to use, and its scope. UseBeanDirective then creates the JavaBean and puts it in the context. If the scope is "application", it stores the bean in a static HashMap for later use; if the scope is "session" it is stored in the user's session.

Also required is a method in VMServlet to work out the content type of the template, in a similar manner to how it is set in a JSP. My current choice is to cache the content types, keyed by the requestURI, and use the getData method of the Velocity template to look for a variable ($contentType) in the page:

private final String determineContentType(
    Node node) {
  for (int i = 0; 
       i < node.jjtGetNumChildren(); 
       i++) {
    StringBuffer sb = new StringBuffer();
    Node n = node.jjtGetChild(i);
    
    if (n instanceof org.apache.velocity.
          runtime.parser.node.ASTSetDirective) {
      Token t = 
        findTokenWithPrefix(n.getFirstToken(), 
                            "$");
      if (t == null || 
         !t.toString().equalsIgnoreCase(
             "$contentType")) {
        continue;
      }
      else {
        t = findTokenWithPrefix(t, "\"");
        String ct = StringUtils.remove(
                      t.toString(), "\"");
        return ct;
      }
    }
  }

  return "text/html";
}

private final Token findTokenWithPrefix(
    Token t, 
    String prefix) {
    
  if (t == null) {
    return null;
  }
  else if (t.toString().startsWith(prefix)) {
    return t;
  }
  else {
    return findTokenWithPrefix(t.next, prefix);
  }
}

This, admittedly, is a hack, but it does the job. I, therefore, add the following to processRequest:

String contentType;
if (contentTypes.containsKey(requestURI)) {
  contentType = 
      (String)contentTypes.get(requestURI);
}
else {
  contentType = 
      determineContentType((Node)tmp.getData());
  contentTypes.put(requestURI, contentType);
}

The final Velocity template then, looks like this:

#set ( $contentType = "text/html" )
<html xmlns="http://www.w3.org/1999/xhtml">

#usebean ( test "java.util.Date" page )
#usebean ( sysm 
  "com.lateralnz.MyMessagesBundle" application )

#parse ( "htmlhead.vm" )

<body>
#include ( "header.vm" )

<h1>
$sysm.getString('app.welcome_title')
</h1>

<p>
$sysm.getString('app.welcome_text')
</p>

<p>$test.getTime()</p>

</body>
</html>

Not a huge difference from the original JSP, as you can see. I've shown the ResourceBundle loaded as a custom-written JavaBean, but in our actual system, I cache the bundle in VMServlet, and load it automatically as an implicit object.

There are a few missing bits and pieces I haven't mentioned. If you're using the JSTL, then you're probably also using some of the other Jakarta taglibs. If that's the case, you may have some additional coding to do. Luckily, it's fairly easy to extend Velocity by creating new directives (in fact, I've found it's easier to come to grips with them than it is to write taglibs, despite the relatively poor API documentation). So far, I've only needed to create a couple of extra directives: one for formatting dates, one for formatting numbers, and a simple directive to halt the rendering of a template (based upon a tip posted in the Velocity mailing lists):

public class ExitDirective extends Directive {
  private static final String EXIT = "exit";
  private static final 
    StopRenderingException STOP_RENDER_EXCEPTION = 
      new StopRenderingException();
  
  public boolean render(
      InternalContextAdapter context, 
      Writer writer, 
      Node node) 
        throws IOException, 
           ResourceNotFoundException, 
           ParseErrorException, 
           MethodInvocationException {
    throw STOP_RENDER_EXCEPTION;
  }
  
  public int getType() {
    return DirectiveConstants.LINE;
  }
  
  public String getName() {
    return EXIT;
  }
}

Within the processRequest method of VMServlet, the StopRenderingException is caught, and effectively halts the template rendering process. In our system, we use this particular directive in the header of a WAP page to drop out, in the case of a user not being logged into the system:

#if ( !$session.getAttribute(USER_ID) )
<card id="message" title="Invalid Session">
<p>Sorry, but your session has 
timed out.</p>
</card>
</wml>
#exit ( )
#end

Conclusion

Velocity provides the web developer with a powerful templating mechanism — however, this can present a significant barrier of entry to those who are interested in migrating away from JSPs and taglibs. Using a simple servlet, and extending Velocity, with its concept of directives, I hope to have shown that you can mimic a considerable proportion of the functionality you get from JSPs, and therefore take your migration one small, low risk, step at a time.

References

One final note: if you're developing on some version of *nix, don't forget the wonders of sed for automating some of the sheer drudgery in porting your JSPs. I'm not a sed expert, but it still only took a few minutes to come up with something that reduced the amount of manual code changes by about 90 percent.

Jason R. Briggs is an architect/developer working in the United Kingdom.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.