JSP 2.0 Simple Tags Explained

JSP 2.0 introduced a lot of new goodies, many of which focus on making the developer’s life easier. In this article, I’ll look at one of my personal favorites: simple tag handlers. Simple tag handlers let you create custom tags that out-perform tag file-based solutions, and are far easier to write than tags based on the previous custom tag API.

As there are now a few different ways to write custom tags, I’ll also provide some pointers on how to decide whether to use simple tag handlers, tag files, and what Sun Microsystems now refers to as ‘classic’ tags.

Let’s kick off by clearing up a potential misconception. The ‘simple’ in ‘simple tag handler’ refers to the ease with which such custom tags can be written, not to any limitations they have. In almost all cases, simple tags are every bit as capable as tags written using the classic tag API, the only caveat being that you cannot include scriptlet code in the body of a simple tag. You can, however, include JSTL tags, EL expressions and other custom actions, so this should rarely, if ever, pose a problem.

First Steps

A simple tag handler subclasses a support class called 'SimpleTagSupport'. This class is a very handy implementation of the 'SimpleTag' interface. It provides implementations of all 5 of this interface’s methods, the most important of which is the doTag() method. The doTag() method in SimpleTagSupport actually does nothing — it’s up to you, the developer, to override this method and code your tag’s functionality. Let’s dive right into an example. The code below shows this method in action:

package demo.tags; 

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;

public class Greeter extends SimpleTagSupport {
   
   public void doTag() throws JspException {
       
       PageContext pageContext = (PageContext) getJspContext();
       JspWriter out = pageContext.getOut();
       
       try {
           
           out.println("Hello World");
           
       } catch (Exception e) {
           // Ignore.
       }
       
   }
}

There’s nothing really fancy here. This class simply uses doTag() to print ‘Hello World’ to the output stream. We will liven this example up as we go, but, for now, there are a few things to take notice of.

The two import statements give us access to all of the required classes; you will need the Servlet and JSP API classes on your classpath for this code to compile. Tomcat users will find these under common/lib as jasper-api.jar and servlet-api.jar.

For the reasons just discussed, this class extends the SimpleTagSupport class and expects us override the doTag() method. Another consequence of extending the SimpleTagSupport class is that a method called setJspContext() was called by the container prior to doTag(), which made the current JSP context information available via getJspContext(). We used this method to get access to the output stream for the JSP.

Mapping Tags to Classes

Assuming this class is installed under /WEB-INF/classes, the next step would be to write a TLD file. The TLD (Tag Library Descriptor) is an XML file that the container uses to map the custom tags in your JSPs to their corresponding simple tag handler implementation classes. Below we see demo.tld, a simple TLD file which, when installed under the /WEB-INF/tlds directory, would map a custom tag called 'greeter' to the class 'demo.tags.Greeter'.

<?xml version="1.0" encoding="UTF-8"?> 
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
 
<tlib-version>1.0</tlib-version>
 <short-name>demo</short-name>
 <uri>DemoTags</uri>

 <tag>
   <name>greeter</name>
   <tag-class>demo.tags.Greeter</tag-class>
   <body-content>empty</body-content>
 </tag>

</taglib>

The 'tlib-version' and 'short-name' elements are straightforward enough, and relate to the tag library version and default tag prefix respectively. The 'uri' element, however, is worth some discussion. When it starts up, the container uses an auto-discovery feature to map all uri element values to the corresponding TLD; therefore, this string must be unique within an application. As of JSP 1.2, we no longer need to make any edits to the web.xml file in order to deploy a custom tag — the auto-discovery feature saves us this inconvenience.

The really interesting part is the contents of the <tag> element. It is in here that, among other things, we can give our tag a name, define its associated tag handler class, and determine whether or not our tag should be allowed to have body content.

The code below shows how we might use our example greeter tag in a JSP.

<%@taglib prefix="t" uri="DemoTags"  %> 

<html>
   <head><title>JSP Page</title></head>
   <body>

<!-- prints Hello World. -->
         <t:greeter />

</body>
</html>

Courtesy of the taglib directive’s 'uri' attribute, we have told the JSP where our TLD file is and, consequently, where our simple tag handler implementation class is located. The 'uri' attribute maps directly to one of the mappings the container created when it started up; the container mapping, in turn, points to the TLD information.

Generally, you won’t need to care about this, but if the uri attribute does not resolve to a container mapping, it is assumed to be a file path. This is useful only when identical uri element values are encountered in different TLD files. If conventions such as using a domain name, or some other unique string, are followed, this should never happen.

Handling Tag Attributes

Custom tags start to become more interesting when you configure them to use attributes. To achieve this, we add instance variables and corresponding property setter methods to the tag handler class. The container calls these setter methods for us, passing along the custom tag attribute values as arguments.

Let us suppose that we want to allow our greeter tag to accept an attribute that will determine who it should greet. We could rewrite the tag handler class to accommodate a 'name' attribute, as shown here:

public class Greeter extends SimpleTagSupport { 
   
   private String name = "World";
   
   public void setName(String name){this.name = name;}
   
   
   public void doTag() throws JspException {
       
       PageContext pageContext = (PageContext) getJspContext();
       JspWriter out = pageContext.getOut();
       
       try {
           
           out.println("Hello " + name);
           
       } catch (Exception e) {
           // Ignore.
       }
       
   }
}

We would also need to update the TLD file to handle our newly defined attribute. The below code shows the relevant portion of the updated TLD:

<tag>
   <name>greeter</name>
   <tag-class>demo.tags.Greeter</tag-class>
   <body-content>empty</body-content>
   <attribute>
       <name>name</name>
       <rtexprvalue>true</rtexprvalue>
       <required>false</required>
    </attribute>
 </tag>

The attribute element sets up an attribute named 'name', states that it is not required, and further states that it will accept 'runtime expression values'. We could then use the tag in any of the following ways:

<t:greeter name="Andy"/> 
<t:greeter name="${param['name']}"/>
<t:greeter />

The first invocation simply prints 'Hello Andy'. As you would expect, the setName() method was called with the string literal 'Andy' as its argument.

The second invocation is similar, but gets its value from the incoming request via an EL expression. You can disable this feature by setting the 'rtexprvalue' element to 'false' or, because false is the default value, by omitting this element altogether.

The last invocation does not use an attribute; instead, it uses the default value of the instance variable 'name'. You can make the attribute mandatory by choosing a value of 'true' for the 'required' element (it is false by default), or you can programmatically test for its existence by testing for null -- whichever makes sense for your application.

Processing Body Content

Custom tags often need access to their body content, and simple tag handlers provide an elegant way to handle this requirement. First, a simple amendment to the TLD is required -- the body-content element needs a value of 'scriptless'. When using 'scriptless', you're allowed to put template text, standard actions and custom actions within your tag's body -- but not java code.

Another important method that is called by the container is setJspBody(). This method makes the tag's body content available as an executable fragment of any EL expressions, custom actions and template text. You access this fragment with the getJspBody() method, and you can execute it using the JspFragment object's invoke() method. The below code this in action:

public void doTag() throws JspException { 
       
       JspFragment body = getJspBody();
       PageContext pageContext = (PageContext) getJspContext();
       JspWriter out = pageContext.getOut();
       
       try {

           StringWriter stringWriter = new StringWriter();
           StringBuffer buff = stringWriter.getBuffer();
           buff.append("<h1>");
           body.invoke(stringWriter);
           buff.append("</h1>");
           out.println(stringWriter);
           
       } catch (Exception e) {
           // Ignore.
       }
       
   }

Let's break this code down. The doTag() method kicks off with a call to getJspBody(), and the resulting JspFragment is stored in a variable called 'body'. JspFragment has an interesting method called invoke(), which takes a java.io.Writer as an argument. Here's the really important bit: when invoke() is called, the fragment is executed and then written to this Writer object.

In simple cases you can supply invoke() an argument of null, causing it to use the current JSP's Writer and, consequently, print the executed fragment directly to the page. In many cases, however, you will want to first process the body content in some way before you send it to the output stream.

One way to do this processing is to use a StringWriter and work with its underlying StringBuffer. As you can see in listing 6, I appended a H1 tag to the buffer, used invoke to execute and write the body content to the writer (and consequently, its underlying buffer), then finished by appending the closing H1 tag. A simple call to out.println() takes care of sending the processed body content on to the output stream.

Once you get the gist of using the executable JspFragment, manipulating body content is a breeze. Keep in mind that you have access to the JSP pageContext object (via getJspContext()), so you can use its setAttribute() and getAttribute() methods to return values to the JSP and otherwise coordinate your tag's functionality with the JSP in which it resides.

Making the Right Choice

Hopefully, you now have a general understanding of how to write custom tags using simple tag handlers. So, is it time to abandon tag files and the classic tag handler API? Here are a few pointers.

If your tag absolutely has to use scripting elements (scriptlets), you will need to use the classic tag approach. This should rarely be an issue, as we have largely replaced scriptlets with the JSTL, custom actions, and EL expressions.

One perceived advantage that classic tags have is that containers can be optimized to provide pooling features for them; simple tag handlers, on the other hand, are instantiated for each occurrence in the JSP page. It is often the case that pooling involves more overhead than it's worth. So, unless you have a tag that creates a lot of expensive resources and is used repeatedly, I wouldn't let this deter you from adopting the simple tag handler approach. In fact, even when multiple invocations do prove to be expensive, you can often stick with simple tag handlers and be a little creative with the PageContext object's ability to hold on to references.

Tag files should be used when you need to generate a lot of mark-up, such as HTML. They are also handy when a more RAD approach to development will suffice. However, keep in mind that they are generally a little slower than tags in compiled form.

When you have lots of Java code and performance is important, simple tag handlers are a great choice. They are easy to write and have more than enough power to get things done.

Summary

We've only scratched the surface of what is a possible using simple tag handlers. I suggest you refer to the JSP specification for more information. You may also want to look at what your IDE has to offer in the way of support for custom tags. Netbeans 4, for example, takes care of writing the TLD file for you, provides custom tag code completion, and has quite a few other features that speed up tag development.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.