Template Engines supported by Niggle


Download:

Latest Version of Niggle (1.04)

Forum example (requires Niggle)



Read More:

Main Page

Niggle Design Goals

Niggle Status

Generated Javadoc

Configuring XML-based Metadata

Subclassing ServletInteraction



Other Links:

Niggle Project Page on Sourceforge

Subscribe to Niggle mailing lists

Preamble

Using some kind of page template solution is really an absolute necessity in serious web application development, since outputting HTML via println() statements in your java code is really not a maintainable practice. I don't think I should launch into a sermon as to why this is so. If you've been there, done that, then you already know this, and if you haven't, then it is wise to believe those who have gone there before you! The Niggle framework currently gives you the choice of three popular open-source template engines: Freemarker, WebMacro, and Velocity. As you may know from reading the history of the Niggle project, Niggle began by leveraging Freemarker. Support for WebMacro and Velocity was introduced fairly recently.

What Niggle offers above and beyond any of these template solutions alone is that it offers you a prebuilt solution for modeling your application's persistent data externally, as well as exposing the data to a page template and reading data from an HTML form. By separating out the persistence layer as well as presentation, Niggle offers the skeletal basis for developing web applications that roughly follow the much-vaunted MVC (model-view-controller) pattern. (And if you don't know quite what that is, well, just think of it as a synonym for GOODtm)

Configuring your Template Engine

In your deployment descriptor (that's a fancy term that means the WEB-INF/web.xml file) you can configure a servlet initialization parameter called PAGE_FACTORY_CLASS. If you intend to use Freemarker, this does not need to be set, since Freemarker is the default. If you intend to use WebMacro, you do have to set this parameter. Within your servlet configuration, you would need something like:

  <init-param>
    <param-name>PAGE_FACTORY_CLASS</param-name>
    <param-value>webmacro</param-value>
  </init-param>
  

Note that "webmacro" above is a convenient shorthand that the code that processes the parameters understands to mean com.revusky.niggle.templates.webmacroimpl.WebMacroPageFactory. Similarly, if you put "velocity" above instead of "webmacro" that is a shorthand for com.revusky.niggle.templates.velocityimpl.VelocityPageFactory. If you write your own PageFactory implementation and wish to specify it in the configuration file, you will have to use the fully qualified class name. If you want to send parameters specifically to the webmacro or velocity template engines, you can do so in your deployment descriptor by defining init params (just like above) that are prefixed with org.webmacro. or with apache.velocity. respectively.

By default, it will be assumed that you wish to load templates relative to the classloader's CLASSPATH. This is the scheme that will allow you to put all of your .class files and associated resources in a .war archive. (This is also GOODTM) However, if you want your templates loaded relative to an absolute location on the file system, you can specify the init parameter RESOURCE_BASE, which specifies a location from which to load the templates. If this is an absolute path, this is assumed to be on the file system. If it is a relative path, then it is assumed to be relative to the classloader. For final deployment, it is far better not to specify an absolute path here, since a relative path (or nothing specified at all) means that your webapp will simply obtain the template files relative to the classloader. This is the most robust, portable solution. However, in a development stage, it may be useful to specify an absolute path here, which points to where your templates are on the file system.

Differences between using Freemarker and WebMacro or Velocity

Under the hood, Freemarker and WebMacro actually take very different approaches to exposing objects to a page template. The Freemarker library defines three basic interfaces for objects that will be exposed to a page. Any object exposed to a page must implement at least one of these three interfaces:

  • freemarker.template.TemplateScalarModel
  • freemarker.template.TemplateListModel
  • freemarker.template.TemplateHashModel

The freemarker engine parses your page template and compiles it into a tree. When it finally outputs the page, it traverses the tree and fills in the elements of the tree based on the variables in your data model. For example, if you have the variable ${foo.bar} in your page template, the template engine expects your root data model to contain a variable called "foo" and it requires that this variable be interpretable as a hash variable. From the engine's point of view, this means that the object implements the TemplateHashModel interface.

Of course, you may have noticed that the Niggle example code makes no reference to TemplateHashModel or any of these interfaces. Also, you can expose a data record onto a freemarker template directly and it gets interpreted as a hash, even though the com.revusky.oreo.Record interface does not inherit from freemarker.template.TemplateHashModel. The reason this works is that Niggle transparently creates a wrapper object that does implement the right interface. In fact, the wrapper object that is used by default is com.revusky.niggle.templates.freemarkerimpl.RecordWrapper. And this object does implement TemplateHashModel. So, what Niggle does behind the scenes, when you expose a Record to a page, is that it actually creates one of these wrapper objects and exposes it. An interesting possibility that you have when you use Freemarker with Niggle (that is unavailable if you use WebMacro) is that you can define your own subclass of RecordWrapper so that certain extra subvariables get exposed. Or conversely, you could actually override the get() method in your RecordWrapper subclass so as to hide or veto certain values from the page template layer. You specify a custom presentation wrapper for a Record type by using the PRESENTATION_CLASS attribute when you define a record in your recorddefs.xml. If you are using WebMacro, this attribute is not used.

By contrast, both WebMacro and Velocity are much more based on java's introspection capabilities. If you have the webmacro variable $foo.bar in a page, WebMacro and Velocity (like Freemarker does) assume that you exposed a variable called "foo" to your root context. However, it takes the reflective lookup approach to deducing what the "bar" subvariable is. It will look for either a getBar() method (javabeans style) or a get(Object obj) method to which it can pass the string "bar". As of this writing, I have not checked which gets priority, if both are available. If you are reading this text and you know the answer, please write me if only to show you're on the ball. :-) (And that I'm not shouting into the wilderness...)

In fact, one thing that WebMacro and Velocity allow for that Freemarker simply does not, is arbitrary calls to public methods by name from a page template. I will not hide from you that I consider this to be rather dangerous. I am still not absolutely sure that this is evil by nature, or merely has the potential for evil. Still, it is possible that, ultimately, this capability is intrinsically evil, in the same way as the rings of power in the Tolkein books. Now, even given that, it is surely not as evil as the ability to embed arbitrary java code in a JSP template. But certainly, it does create the potential for people working on the page design layer to rely on the existence of arbitrary methods in your core java objects. This can create an undesirable coupling, where, whenever you want to refactor your java code, you have to ask whether a method that you wish to eliminate or rename is actually used somehow in a page template. And in fact, given the ability for variable aliasing that page templates provide, it could be fairly difficult to know for sure whether a method is being used.

Summary

You can use either Freemarker, WebMacro or Velocity in conjunction with Niggle. Freemarker is the default; it works well and the freemarker bridge code has been subject to much more usage and testing. Broadly speaking, you can use either one almost indistinctly as a java programmer, since they are wrapped up in a higher level API. The biggest difference will naturally be at the page template level, since the template languages differ significantly.

Probably, the biggest practical consideration in choosing which one to use will be whether the people working at the page design level already have experience with one or the other, or have a stated preference. If there is no stated preference, typically because your designers have used neither one, you might as well go with the default, which is Freemarker.

In the above, I point out a couple of basic differences. First of all, if you use the Freemarker bridge code, you have the possibility of implementing your own custom subclass of RecordWrapper. This can give you an extra degree of freedom when it comes to migrating your java code, without having to change any template code. On the other hand, the reflection-based approach of WebMacro or Velocity is appealing in certain ways, since it gives you access to arbitrary java methods from your page template. Though, you will note that I express my reservations about in that above.

Since it is not terribly difficult to write the bridge code that allows Niggle to work with other page template solutions, I anticipate that Niggle will at some point offer a greater choice of presentation solutions than it does now. Probably something that uses XML/XSL as well as a solution that uses JSP taglibs. Since XSL and JSP are standard, "approved" ways of doing things, there will likely be a fair bit of third-party tool support coming on line.


This is a first draft of a document explaining Niggle's template engine support. If you note any inaccuracies in this document, or have any suggestions for improvement, please write me! I do not document the syntax of the template languages here. At some point soon, I anticipate including a quick-start crib sheet that explains the template language syntax of Freemarker, WebMacro and Velocity, and their usage with Niggle. Currently, for this information, please refer to the respective websites: freemarker.sourceforge.net, www.webmacro.org, and jakarta.apache.org/velocity.

Jonathan Revusky 15 July 2001

Sourceforge Logo