ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Advanced Features of JSP Custom Tag Libraries

by Sue Spielman
01/18/2001

In this article, the second in the JSP custom tag libraries series, we will cover advanced JSP features and how to use them. If you aren't familiar with tag libraries, spend a few minutes reading the previous article, Designing JSP Custom Tag Libraries.

In this article you'll learn about

  • JSP container interaction with tags,
  • tags with bodies,
  • nested tags,
  • tag extra info, and
  • cooperating tags.

All of the classes and interfaces discussed herein are from the javax.servlet.jsp or javax.servlet.jsp.tagext package.

The JSP container interaction with tags

When using advanced features of tag libraries it is important to have a basic understanding of how the JSP container interacts with the Tag handler classes. We'll look at this first before moving to using advanced features.

Tag Interface
The Tag interface defines the basic protocol between a tag handler and JSP container. It defines the life cycle and the methods to be invoked when the start and end tag of an action are encountered.

The JSP container invokes the setPageContext, setParent, and attribute setting methods before calling doStartTag. The container also guarantees that release will be invoked on the tag handler before the end of the page. A typical tag handler method invocation sequence from the JSP container might look like:

SomeTag tag = new someTag();
tag.setPageContext(...);
tag.setParent(...);
tag.setAttribute1(value1);
tag.setAttribute2(value2);
tag.doStartTag();
tag.doEndTag();
tag.release();

The release method of tag handler should reset its state and release any private resources that it might have used. Usually it isn't necessary to override the base class implementation.

Body Tag Interface
If a tag is derived from BodyTagSupport, there are additional methods -- setBodyContent, doInitBody, and doAfterBody -- contained in the BodyTag interface. The additional methods let a tag handler access its body. The body of a tag is anything that comes between the start and end of a tag.

The BodyContent object is a subclass of JspWriter, which is the writer used internally for the JSP out variable. The BodyContent object is available through the bodyContent variable in doInitBody, doAfterBody, and doEndTag. This is important because the BodyContent object contains methods that you can use to write, read, clear, and retrieve content and then incorporate that content into the original JspWriter during the doEndTag. We'll show this later when we go through a code sample.

The setBodyContent creates a body content and adds it to the tag handler. The doInitBody is called only once before the evaluation of the tag body and is typically used to perform any initialization that depends on the body content. The doAfterBody is called after each evaluation of the tag body. If doAfterBody returns EVAL_BODY_TAG, the entire body is evaluated again. Otherwise SKIP_BODY is returned to indicate that the tag is finished with its body evaluation.

A typical tag handler derived from BodyTagSupport would have the following method invocation sequence from the JSP container:

tag.doStartTag();
out = pageContext.pushBody();
tag.setBodyContent(out);
// perform any initialization needed after body content is set
tag.doInitBody();
tag.doAfterBody();
// while doAfterBody returns EVAL_BODY_TAG we 
// iterate the body evaluation
...
tag.doAfterBody();
tag.doEndTag();
tag.pageContext.popBody();
tag.release();

Using tags with bodies

Using a tag that evaluates a body allows for looping. This can be a powerful feature for dealing with database result sets or other types of data that are enumerations. When defining a tag that uses a body, there are two basic steps. First the tag definition in the TLD file needs to set <bodyContent> to either JSP or tagdependent. JSP indicates that the body is evaluated by the JSP container and then is possibly processed by the tag itself. Tagdependent indicates that the body is only processed by the tag. The TLD element looks like

<tag>
   ...
   <bodycontent>JSP|tagdependent</bodycontent>
</tag>

The second step is to derive the tag from BodyTagSupport. BodyTagSupport is the helper class of the BodyTag interface, so technically you don't have to implement any of the methods since there are default implementations. However it's more than likely that you will want to override one or all of the methods to do specific processing for your tag.

How one implements a tag handler for a tag with a body depends on whether the tag handler needs to interact with the body or not, where "interact" means that the tag handler reads or modifies the contents of the body or causes iterative evaluations of the body. If the tag handler interacts with the body, the return value SKIP_BODY indicates that the JSP container should not evaluate the code within the body of the tag. The EVAL_BODY_TAG value is only possible if the class extends BodyTagSupport. If the tag handler does not need to interact with the body, the tag handler should implement the Tag interface (or be derived from TagSupport). If the body of the tag needs to be evaluated, the doStartTag method must return EVAL_BODY_INCLUDE; otherwise it should return SKIP_BODY. If the tag handler needs to interact with the body, the tag handler must implement BodyTag (or be derived from BodyTagSupport). Such handlers typically implement the doInitBody and the doAfterBody methods. These methods interact with body content passed to the tag handler by the JSP container. The doStartTag method needs to return EVAL_BODY_TAG; otherwise it should return SKIP_BODY.

Let's put a quick sample together that shows tag interaction with a body. This will include the TLD tag definition, the tag handler, and finally the JSP page that calls the tag. For a complete example of the TLD file, refer to part one of this series (JSP Customer Tag Libraries).

The definition of this tag in the TLD file looks like

<tag>
  <name>paramLoop</name>
  <tagclass>oreilly.examples.ParamLoopTag </tagclass>
  <!-- Allow for a body to be included for this tag -->
  <bodycontent>JSP</bodycontent>
  <info>
  This is a simple tag to demonstrate how to include 
  and evaluate the body of a tag
  </info>
  
  <!-- Required attributes  -->
  <attribute>
    <name>enum</name>
    <required>true</required> 
    <rtexpvalue>true</rtexpvalue>
  </attribute>
</tag>

The following sample is a tag handler that displays all of the request parameters in a table and interacts with the body:

import java.util.Enumeration;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.IOException;
 
public class ParamLoopTag extend BodyTagSupport{
    Enumeration enum=null;
    
public void setEnum(Enumeration enum){
  this.enum = enum;
}
public int doStartTag() throws JspException{
  return EVAL_BODY_TAG;
}
    
public void doInitBody() throws JspException{
  if (enum != null){
  if (enum.hasMoreElements()){
     pageContext.setAttribute("nextParamName",
                 enum.nextElement());
   }
}
public void doAfterBody() throws JspException{
  if (enum.hasMoreElements()){
     pageContext.setAttribute("nextParamName",
                               enum.nextElement());
     //Continue looping
      return EVAL_BODY_TAG;
   } else {
     // We're done
     return SKIP_BODY;
   }
}

public int doEndTag() throws JspException{
  try {
    // Get the current bodyContent for this tag and 
    //write it to the original JSP writer
    pageContext.getOut().print(bodyContent.getString());
    return EVAL_PAGE;
  }
  catch (IOException ioe){
    throw new JspException(ioe.getMessage());
  }
}
}

The JSP code that uses this tag might look like

<html>
<body>

<%@ taglib uri="/oreillySample.tld" prefix="sample" %>
<h1>Sample Tag using Body Interaction</h1>
<table border="2">
<tr>
	<th>Parameter Name</th>
	<th>Parameter Value</th>
<sample:paramLoop enum="<%=request.getParameterNames() %>" >
	<tr>
<%-- 
For simplicity, we are not doing any error 
checking here, and just printing the first value.
 --%>
<% String paramName = 
   (String)pageContext.getAttribute("nextParamName"); %>
<td> <%= paramName %> </td>
<% String[] paramValues = 
   request.getParatmerValues(paramName); %>
<td><%= paramValues[0] %>
	</tr>
</sample:paramLoop>
</table>
</body>
<html>

The table output produced from this tag executing assuming two incoming parameters (person and company) would look like

Parameter Name Parameter Value
person Sue
company Switchback Software

Pages: 1, 2

Next Pagearrow