Classic Tags Overview
There are now essentially three different mechanisms for building custom tags outlined in the JSP 2.0 specification: tag files, simple tags, and classic tags. A tag files provide a natural progression for abstracting content away from JSP pages and into reusable components.
Because tag files allow this content to be written using regular JSP constructs, it makes wrapping up content fairly straightforward and usable for JSP developers who might not necessarily know the Java programming language.
The next step up, in terms of complexity, is simple tags. These tags allow reusable functionality to be defined with Java code and used on the page as a custom tag. Although this process does require familiarity with the Java programming language, it allows complex behavior to be wrapped up and used by page authors in a straightforward way.
Simple tags offer a great deal of flexibility in the way that the functionality is encapsulated and, through body content and JSP fragments, they allow separation between content and content presentation. So, what are classic tags and why do you need them?
Classic tags are the original tag development methodology introduced in version 1.1 of the JSP specification. JSP 1.2 then added new functionality and simplified the programming model slightly, but essentially the model was the same, and it remains the same in the JSP 2.0 specification.
As with simple tags, classic tags use the concept of a tag handler class that is written using Java code. This is then described with a tag library descriptor file in the same way it is with simple tags, and the resulting custom tags are again used in the same way. So, what differentiates classic tags from simple tags?
The Differences Between Simple and Classic Tags
There are several key differences between simple and classic tags. Let's take a quick look at each in turn and evaluate what it means to you as a tag developer.
The Tag Handler Interface
The fundamental difference between simple and classic tags is the way in which the tag handler class is implemented. With simple tags, any tag handlers you build must implement the javax.servlet.jsp.tagext.SimpleTag interface. With classic tags, however, the tag handlers must implement the javax.servlet.jsp.tagext.Tag interface or, as you'll see in this article, one of its subinterfaces.
For you as a developer, this means you need to learn a slightly different programming model. For example, with simple tags all of the functionality to be encapsulated within the tag is defined within the doTag() method.
With classic tags, there are two methods you must implement: doStartTag() and doEndTag().
Feedback from tag developers over the past couple of years has been mixed, and many people find the concepts employed by classic tag handlers confusing. For this reason, the interface has been simplified and simple tags are the result of this process.
Another key, though often neglected difference relates to the tag life cycle. With simple tags, an instance of the tag handler class is created when needed and that instance is only used to serve a single invocation of a custom tag.
In other words, a unique tag handler instance is created for each usage of a simple tag on a page. With classic tags, this may or may not be the case because the JSP specification provides the ability for container vendors to optionally improve classic tag performance by pooling and reusing tag handler instances.
This means that, for example, a single tag handler instance could be created and reused to service all invocations of that custom tag per page.
The rules around reuse are fairly complicated, and to make matters worse, JSP container vendors don't always choose to implement this optional piece of the specification. Therefore, if you're not aware of the implications that this has, your tags may not function correctly on all vendors' JSP containers.
The problems that arise between different JSP contains is another reason simple tags were introduced and it's another area in which the complexity associated with developing them has been reduced.
The downside is that there may be times when you'd like tag instances pooled and reused. For example, your tag might acquire some expensive resource when it is created.
In this example, it makes sense to take advantage of any performance benefits that the container may provide. In many scenarios, however, this just isn't an issue and simple tags are more than adequate.
With our brief look at the differences between simple and classic tags over, let's take a look at the interface that classic tag handlers must implement.
The Tag Interface
The Tag interface, like the SimpleTag interface, provides the basic contract that must be upheld between the JSP page and the tag. The following code snippet shows the interface:
package javax.servlet.jsp.tagext;
import javax.servlet.jsp.JspException;
public interface Tag {
public final static int SKIP_BODY = 0;
public final static int EVAL_BODY_INCLUDE = 1;
public final static int SKIP_PAGE = 5;
public final static int EVAL_PAGE = 6;
void setPageContext(PageContext pc);
void setParent(Tag t);
int doStartTag() throws JspException;
int doEndTag() throws JspException;
void release();
}
The Tag Life Cycle
The life cycle of simple tags consists of the tag handler being created, contextual and environmental information being passed to it, and finally, the doTag() method being executed. As the following diagram shows, classic tags are not too different in this respect:
As with simple tags, when a custom tag is used on a JSP page, the first thing that the JSP container must do is create a new instance of the tag handler class. Again, this is performed by invoking the default, no arguments constructor.
Setting the Context
The next step in the tag life cycle is to make the tag handler instance aware of the environment in which it is running. This involves passing the tag handler a reference to the current PageContext through the setPageContext() method.
Like JspContext, this method provides an easy way to access other objects such as the current output writer and scoped attributes. Notice here that it's a PageContext instance that gets passed to the tag and not a JspContext instance, as happens with simple tags.
This moves simple tags away from being dependent on services and features provided by the Java Servlet API; PageContext uses such features, whereas JspContext doesn't. However, for consistency and backwards compatibility with earlier versions of the JSP specification, PageContext actually extends JspContext.
Setting the Parent
Like simple tags, classic tags can be nested, it's possible for custom tag handlers to cooperate and communicate with one another. As an example, a child (nested) tag may ask for information from its parent tag. For this reason, the setParent() method is called and passes a reference to the closest enclosing tag handler or null if the tag isn't nested.
With simple tags, this reference is of type JspTag (the superinterface for all tag handlers), but again for backwards compatibility with classic tags this reference is of type Tag (a subinterface of JspTag).
Executing the Functionality
With the context set, the next thing to do is execute the functionality provided by the tag. With simple tags, this involves calling the doTag() method. With classic tags, this means calling the doStartTag() and doEndTag() methods.
When you looked at how custom tags can be used on the page, you saw that they can be written in a long or shortened form as follows:
<prefix:myTag></prefix:myTag>
<prefix:myTag/>
With the long format you explicitly write the starting (opening) and ending (closing) tags, and with the shortened format you combine them. Regardless of how you write them, both the doStartTag() method and the doEndTag() method are called on a tag handler instance.
The following diagram shows the tag life cycle from a slightly different viewpoint and illustrates the way in which the doStartTag() and doEndTag() methods can also affect the tag life cycle by the values that are returned by them:
The Start Tag
The doStartTag() method is called when the starting tag is encountered on the page:
int doStartTag() throws JspException;
The method signature from the Tag interface states that a primitive int value is to be returned. This signals to the JSP page what do to next. Two values can be returned from tags implementing this interface, SKIP_BODY and EVAL_BODY_INCLUDE, which are defined as constants within the Tag interface.
Returning SKIP_BODY signals to the JSP page that after the doStartTag() method has been called, any body content for the tag should be ignored. For example, any body content such as JSP code, Java code, or content that would normally be output to the page is simply dropped. Following this, processing proceeds to the doEndTag() method.
On the other hand, returning EVAL_BODY_INCLUDE from the doStartTag() method signals that any body content should be evaluated and output to the page.
The End Tag
The doEndTag() method is called when the ending tag is encountered on the page, again, regardless of whether the tag is written on the page using the long or shortened format:
int doEndTag() throws JspException;
This method also specifies an integer return type, and the valid return types for this method are the other two constants defined within the Tag interface, SKIP_PAGE and EVAL_PAGE. Here, these return values signal whether or not the JSP container should continue evaluating the rest of the JSP page.
In reality, the SKIP_PAGE return value is rarely used, because there aren't many circumstances in which you'll want to actually stop the rest of the page from being processed. One such example might be a security tag that appears at the top of the JSP page and checks whether or not the current user is authorized to see the contents of the page.
Releasing State
The final method to be called as part of the tag life cycle is the release() method. This method is called to ask the tag handler to release any state it may be storing, and it's only called on the tag handler when that tag handler instance is finished and won't be used anymore.
In other words, with JSP containers that don't support the optional pooling of tag handler instances, the release() method is called after the doEndTag() method because that particular instance will never be used again.
On the other hand, with containers that provide instance pooling, this method is only called when the container has finished using the instance and before it gets garbage collected.
Important
It's a common misconception that release() is always called directly after the doEndTag() method and hence used to clear the values of instance variables. This is not the case, and relying on this behavior means that your tags might not work as expected in all vendors' JSP containers. The next article looks at some of the best practices for using and taking advantage of the tag life cycle.
Although the Tag interface contains more methods than the SimpleTag interface, providing an implementation is trivial.
However, for convenience and in the same way that the SimpleTagSupport class provides a default implementation of the SimpleTag interface, the JSP specification provides the TagSupport class that you can use as a starting point for your own tag handlers. Here, the default implementations of the doStartTag() and doEndTag() methods return SKIP_BODY and EVAL_PAGE, respectively.
Building the DateTime Tag Handler
The first step is, of course, to build the tag handler class. You'll use the TagSupport class as a starting point:
package com.apress.projsp20.ch06.tagext;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;
public class DateTimeTag extends TagSupport {
Next is the functionality provided by the tag, which you've implemented within the doStartTag() method:
Important
In situations in which it doesn't matter whether a custom tag will be used in the long or shortened form on the page, the functionality associated with the tag can be implemented within either the doStartTag() or the doEndTag() method.
public int doStartTag() throws JspException {
DateFormat df = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM);
try {
pageContext.getOut().write(df.format(new Date()));
} catch (IOException ioe) {
throw new JspTagException(ioe.getMessage());
}
return SKIP_BODY;
}
}
As you can see, the code that provides the functionality of the tag is pretty much the same as that you used with the simple tag example in the previous article. The only real difference is in the way that this code is packaged within the tag handler.
One point to note here is that unlike the doTag() method on the SimpleTag interface, the doStartTag() and doEndTag() methods don't declare that they throw IOException, therefore you must catch and handle this exception.
In this example, you're throwing a JspTagException (a subclass of JspException) to tell the JSP page that something went wrong. Throwing a JspTagException instead of a more generic JspException is a useful way to specify that the problem may be related to a custom tag rather than the page itself, during development and debugging.
Finally, as you're not interested in the body content of this tag, you return SKIP_BODY from the doStartTag() method.
With the tag handler written, the next step is to describe the tag. As with simple tags, you do this using the tag library descriptor (TLD) file:
<?xml version="1.0" encoding="UTF-8" ?>
<taglib 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"
version="2.0">
<description>
Tag library for Professional JSP 2.0, Article 6.
</description>
<jsp-version>2.0</jsp-version>
<tlib-version>1.0</tlib-version>
<short-name>ch06</short-name>
<uri>http://www.apress.com/projsp20/ch06</uri>
<tag>
<name>datetime</name>
<tag-class>com.apress.projsp20.ch06.tagext.DateTimeTag</tag-class>
<body-content>empty</body-content>
<description>
Outputs the current date and time to the page.
</description>
</tag>
</taglib>
As this code illustrates, describing a tag with a TLD file is the same regardless of whether that tag is a simple tag or a classic tag.
Note
The way in which custom tags are used on the page provides a nice abstraction for those tags and the way they're built. It's perfectly acceptable to mix simple and classic tags together in the same TLD file, and page authors will never know how the tags are actually implemented. In fact, as you'll see in the next article, it's also possible to describe tag files in the TLD, which makes it possible to wrap up any type of tag for easy reuse.
Using the DateTime Tag
If you assume that the TLD file has been saved at the location /WEB-INF/tlds/ch06.tld, then importing and using the tag library is the same as before:
<%@ taglib uri="/WEB-INF/tlds/ch06.tld" prefix="ch06" %>
The current date and time is <ch06:datetime/>
Not surprisingly, the results of using the tag are also the same as before. As this example shows, building trivial classic tags isn't that much different from building the simple tags you saw in the previous article, and essentially it's just a matter of using the Tag interface and implementing your functionality within the doStartTag() or doEndTag() method. Let's now continue our tour of classic tags by seeing how they, like other custom tags, can be customized with attributes.