Ursine rambling and grumbling

In a recent thread in the JavaRanch JSP forum, the subject of JSP 2.0 tag files came up, and there was much teeth-gnashing over the fact that this wonderful mechanism was not available for JSP 1.2 environments.

As anyone who's followed my posts in that forum would be aware, I detest putting HTML markup in Java code almost as much as I hate putting Java code in JSP pages. So while I was working in the JSP 1.2 environement I came up with a mechanism that allowed me to easily include JSP fragments from the Java code of tag handlers.

I dubbed this mechanism "taglets" and, for those who must still operate within the JSP 1.2 environment for the time being, I figured I'd present that mechanism here.

The TagletTagSupport Class

The major component of the taglet mechanism is a fairly simple base class that tag handlers extend (in lieue of TagSupport) that provides a protected method to include a named resource. It's fairly simple (the most complicated part being the conversion of exceptions) and is presented below in its entirety:

package org.bibeault.tags;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * Tag support class that enables deriving tags handlers to emit 'taglets':
 * JSP fragments that can be used to emit markup from a tag without having to
 * build the markup in Java code.
 *
 * The emitting tag is made available to the taglet via the key defined by
 * KEY_EMITTING_TAG.
 */
public class TagletTagSupport extends TagSupport {
    
    public static final String KEY_EMITTING_TAG = "emittingTag";
    
    /**
     * Includes the JSP fragment identified by the passed resource. By convention
     * such resources are usually stored in /WEB-INF/taglets
     *
     * @param resource the context-relative path to the JSP fragment resource
     */
    protected void includeTaglet( String resource ) throws JspException {
        pageContext.setAttribute( KEY_EMITTING_TAG, this, PageContext.REQUEST_SCOPE );
        try {
            pageContext.include( resource );
        }
        catch (IOException e) {
            throw new JspException( "I/O error including taglet with resource of " + resource + ": " + e.getMessage(), e );
        }
        catch (ServletException e) {
            Throwable rootCause = (e.getRootCause() != null) ? e.getRootCause() : e;
            throw new JspException( "Error including taglet with resource of " + resource + ": " + rootCause.getMessage(), rootCause );
        }
    }
    
}

Note that the includeTaglet() method establishes the emitting tag as a request-scoped variable named emittingTag. This makes it really easy to use the JSTL and EL in the taglet to obtain any attributes of the tag that are exposed as properties.

A Simple Example Tag

The ExampleTagletTag is a simple example of a taglet-enabled tag that has a single attribute that it passes to its taglet fragment:

package org.bibeault.tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

/**
 * Simple sample tag using the taglet support base class. A single tag attribute
 * named whatever is passed to the taglet as a scoped variable.
 * This attribute is also exposed as a property should the taglet prefer to
 * reference the attribute value via the EL.
 *
 * Note: in a non-example class I would never hard-code the path to the resource
 * as done here. Rather, I'd get the base path to the taglet resource from a
 * context variable or other such property.
 */
public class ExampleTagletTag extends TagletTagSupport {
    
    public static final String KEY_WHATEVER = "whatever";
    
    private String whatever;
    
    public void setWhatever( String value ) { this.whatever = value; }
    public String getWhatever() { return this.whatever; }
    
    public int doStartTag() throws JspException {
        pageContext.setAttribute( KEY_WHATEVER, this.whatever, PageContext.REQUEST_SCOPE );
        includeTaglet( "/WEB-INF/taglets/example-tag.jsp" );
        return EVAL_BODY_INCLUDE;
    }
    
}

The Simple Example Taglet

A super-simple example of how the taglet file example-tag.jsp could be implemented is:

<%--
  Simple example taglet for the ExampleTagletTag tag handler. Normally I would
  use the JSTL and EL in such files, but for simplicity this example sticks
  to scriplet expressions.
--%>
<%@ page import="org.bibeault.tags.ExampleTagletTag" %>

<div>
  This could be any markup you want it to be.
</div>
<div>
  The value of the <b>whatever</b> attribute is: <%= request.getAttribute( ExampleTagletTag.KEY_WHATEVER ) %>
</div>

Alternatively, and preferably, the taglets would utilize the JSTL and EL rather than scriplet expressions (but I didn't want to cloud this example with JSTL setup). Under JSTL, accessing the attribute would become:

<c:out value="${emittingTag.whatever}"/>

and it would not have been necessary for the ExampleTagletTag class to export the attribute as a scoped variable.

Using the Simple Example Tag

Assuming we brought the TLD for the tag set containing our example tag into a page using the prefix xyz and the tag name example, using the tag is as simple as:

<xyz:example whatever="Hi there!"/>
    

which results in the following on the page:

This could be any markup you want it to be.
The value of the whatever attribute is: Hi there!