Overview | Examples | Writing templates | Java interface
API docs | Download
 

Using JSTE in Java applications

JSTE implements three essential abstractions: the template (class Template), the repository (interface Repository) and the template cache (class TemplateCache).

The Template class

The Template class represents a template. One way to create a template instance is to use its two-parameter constructor that takes a template string and a template name as parameters:
    Template template = new Template (templateString, "myTemplate");
The template name (myTemplate in this example) is used when reporting errors, and assumes significance when including templates within other templates. The most significant Template method is expand, used to expand the template into a Writer. For example:
    template.expand (context, responseWriter);
The second parameter is a standard java.io.Writer instance into which output is written. The first parameter provides data context for the template expansion. You would usually want this first parameter to be a java.util.Map containing the data values used in the template expansion. This Map can be arbitrarily complex in the sense that it can contain nested Maps, Lists, arrays or other Java objects. The expand method wraps this Map into the Javascript environment with simplified access.

The Template object represents a single template that does not know of the existence of other templates. Therefore, you cannot use document.include in templates expanded by this class. To enable such includes, you must use the cached template.

The Repository

The repository abstraction, represented by the Repository interface, is a collection of template strings accessible by template name. One implementation of this interface is the FileRepository class, which views the contents of files in the subtree rooted at a particular directory as template strings, with accessible by file name. For instance, you would create a FileRepository instance via
    FileRepository fr = new FileRepository ("/web/templates");
Then fr would make all the files under /web/templates available via the Repository interface.

The template cache

The TemplateCache object caches the templates available from a repository. Cached templates are maintained in pre-parsed format, so that we only incur the parsing cost the first time a template is accessed. The TemplateCache uses a lazy caching mechanism to ensure that, whenever the template in the repository has been modified, the cached copy is automatically refreshed.

Example 1: expanding a single-file template

Here is a simple, complete Java program that reads a template from standard input, expands the template and writes the results to standard output.
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import jstl.template.Template;

public class Example1 {
    
    public static void main (String[] args) throws Exception {
        // Create a template that is read in from System.in
        Template t = new Template (new InputStreamReader (System.in));

        // Expand it and write the result to System.out
        t.expand (null, new OutputStreamWriter (System.out));
    }
}
    
In this example, the first parameter to the expand method is null, so the template cannot make any references to the templateContext object. Also note that, since this example does not use a TemplateCache, the template cannot include any calls to document.include.

Here are the steps to execute this program:

Example 2: expanding nested templates

In practice, you would create templates from the files in some sort of repository, such as the file system or the contents of a jar file. To accomplish this, you must first create a TemplateCache instance that will provide access to templates from the repository.

To initialize a templating system using files from the file tree rooted at /web/templates, we would say:

      TemplateCache cache = new TemplateCache (new FileRepository ("/web/templates"));
Subsequently, each time we need a template to expand, we would retrieve it from the cache and expand it into a Writer, using a suitable template context:
      Template template = cache.getTemplate (templateFileName);
      Map templateContext = new HashMap();
      // Set up templateContext with a suitable contents here....
      template.expand (templateContext, outputWriter);
    

To illustrate these ideas, here is a complete program that uses the FileRepository and TemplateCache classes.
import java.io.OutputStreamWriter;

import java.util.*;

import jstl.template.FileRepository;
import jstl.template.Template;
import jstl.template.TemplateCache;

public class Example2 {
    
    public static void main (String[] args) throws Exception {
        // Create a template cache that holds files in the current directory.
        TemplateCache tc = new TemplateCache (new FileRepository ("."));

        // Get a template from the cache.
        Template t = tc.getTemplate (args[0]);

        // Prepare a templateContext
        Map aMap = _prepareContextMap ();

        // Using the templateContext, expand the template to System.out
        t.expand (aMap, new OutputStreamWriter (System.out));
    }
    
    private static Map _prepareContextMap () {
        Map<String, Object> aMap = new HashMap<String, Object>();
        aMap.put ("aString", "This is a string.");
        aMap.put ("number", new Integer (433));
        aMap.put ("aList", Arrays.asList (new Object[] {"Foo", 937, new Date()}));
        aMap.put ("anArray", new Object[] {5, 13, new Date(), 11});

        Map<String, Object> subMap = new HashMap<String, Object>();
        aMap.put ("subMap", subMap);
        subMap.put ("date", new Date());

        return aMap;
    }
}

Caching and performance

The JSTE TemplateCache class uses a “lazy cache” mechanism for caching. It maintains a map whose keys are resource names from its repository, and the correspinding values are pre-parsed Template instances. When a client invokes the getTemplate method to request a Template instance, the TemplateCache checks whether there is an entry in its map, and if so, whether the entry has a modification date no later than that of the corresponding resource in the repository. If either of these conditions fails, the TemplateCache refreshes the template from the repository and parses the template again. This mechanism has several benefits: