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!