Supporting a template language

Tiles currently uses JSP, FreeMarker and Velocity as a template language. All these view technologies are supported through the tiles-template module.

This module contains all the common code for directive-based template languages, JSP and FreeMarker have this characteristic, because they use "tags" as directives. Velocity support uses both a "directive-based" style and a Java-like style.

Creating directives

To create a directive in your favourite template language, simply use all of the classes in org.apache.tiles.template package. These classes (called "models) have three methods:

  • start: to be called at the start of the directive;
  • end: to be called at the end of the directive;
  • execute: when you need to use directives with and without body.

Typically, you need only to use start and end methods (this is the case of JSP and FreeMarker), but sometimes you need execute to: Velocity support uses execute methods to have a clearer syntax when no body is provided.

For example, the InsertAttributeModel model can be implemented in FreeMarker this way:

@SuppressWarnings("unchecked")
public void execute(Environment env, Map params, TemplateModel[] loopVars,
        TemplateDirectiveBody body) throws TemplateException, IOException {
    Map<String, TemplateModel> parms = (Map<String, TemplateModel>) params;
    TilesContainer container = FreeMarkerUtil.getCurrentContainer(env);
    model.start(
            FreeMarkerUtil.getComposeStack(env),
            container,
            FreeMarkerUtil.getAsBoolean(parms.get("ignore"), false),
            FreeMarkerUtil.getAsString(parms.get("preparer")),
            FreeMarkerUtil.getAsString(parms.get("role")),
            FreeMarkerUtil.getAsObject(parms.get("defaultValue")),
            FreeMarkerUtil.getAsString(parms
                    .get("defaultValueRole")), FreeMarkerUtil
                    .getAsString(parms.get("defaultValueType")),
            FreeMarkerUtil.getAsString(parms.get("name")),
            (Attribute) FreeMarkerUtil.getAsObject(parms
                    .get("value")), env);
    FreeMarkerUtil.evaluateBody(body);
    model.end(FreeMarkerUtil.getComposeStack(env), container,
            FreeMarkerUtil.getAsBoolean(parms.get("ignore"), false), env);
}

As you can see, the map of parameters at the call of the tag are converted into parameters of the template model. In JSP, private fields would have become such parameters. The FreeMarkerUtil.evaluateBody(body) is used to evaluate the body, and all of its tags, so the model can start and end correctly.

The role of the compose stack

In the previous code you may have noticed the "compose stack" parameter. This stack is simply a Stack of objects that is used to store objects that need to be composed between the start and the end of the model.

For example, if you use DefinitionModel, the Definition object is created in start and pushed into the stack and it is retrieved in end. Between these methods, other models, like PutAttributeModel, can get it from the stack and modify it.

Developing attribute renderers

Template language files (for example ".ftl" files for FreeMarker, ".vm" for Velocity can be rendered using specific servlets (respectively FreemarkerServlet and VelocityViewServlet).

However, Tiles attributes can be rendered through the use of attribute renderers. Currently, the implementations for Velocity and FreeMarker use their servlets as delegate for the real rendering. This means that these renderes use the servlet directly through calling some specific method, and not forwarding to an URL.

This is particularly useful if the attribute to render points to a file not accessible as an URL, but allowing the correct initialization of these attributes and their integration with the rest of the layout.