ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


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

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

Nested tags

Nested tags are another powerful feature of tag libraries. You can use nested tags whether the tag implements the Tag or BodyTag interface. When tags are nested, it's possible to obtain a reference to the parent class by using the findAncestorWithClass method. By casting the reference to the parent class, the nested tag handler can call methods in the parent class. Another use for the findAncestorWithClass is to determine what type of tag in which another tag might be nested. It is likely, when building more complicated systems, that a tag handler might need to act differently depending on what the outer tag is. The tag handler can determine what action to take based on the return value of the findAncestorWithClass. Nested tags really only effect the tag handler, so I will not reiterate TLD and JSP code here.

The following sample demonstrates the concepts we just discussed:

public class OuterTag1 extends TagSupport {
…
  public void setMyValue(MyClass arg) { ... }
  public MyClass getMyValue() { ... } 
}
public class OuterTag2 extends TagSupport {
…
  public void setSomeOtherValue(MyClass arg) { ... }
  public MyClass getSomeOtherValue() { ... } 
}

public class InnerTag extends BodyTagSupport {
  public int doStartTag() throws JspTagException {
    OuterTag1 parent1 =  
(OuterTag1)findAncestorWithClass(this, OuterTag1.class);
    OuterTag2 parent2 = 
(OuterTag2)findAncestorWithClass(this, OuterTag2.class);


    if (parent1 != null) {
      parent1.setMyValue(...);
    } 
    if (parent2 != null){
              // make a different method call
	MyClass value = parent2.getSomeOtherValue(…);    
	// Take a different action here for tag 
      // processing…
      // do something with the value
   }
    return(EVAL_BODY_TAG);
  }
  ...
}

TEI

The Tag Extra Info (TEI) Class is used to enable scripting variables and also to perform attribute validation. The TEI is a Java class that extends the TagExtraInfo class. The method used is the getVariableInfo; it takes a TagData parameter and returns an array of VariableInfo. The TagData parameter contains the name-value pairs of attributes which can be used for scripting variables.

The main purpose of the getVariableInfo method is to create an array of VariableInfo objects. You create one VariableInfo object for each scripting variable that you will be using. When new'ing a VaribleInfo object the following are defined:

The scope of variables in tags can be defined as one of the following:

One more aspect of TEI worth mentioning is that it can be used for attribute validation. A TEI class can optionally override the isValid method to implement tag-specific attribute validation. A TagData instance is passed to the isValid method at JSP translation time. Therefore if an attribute allows runtime evaluation, the isValid method can't perform a validation on it.

Let's step through an example that describes the TEI class, the handler, and the JSP that would use the tag. I will skip over the TLD file since it is just a matter of using the <teiclass>classname<teiclass> within the tag definition.

The scripting variables loopCounter, loopCounter2, and loopCounter3 are defined in the following TEI class to demonstrate the differences that scope makes. It is possible that the information necessary to construct the Variable info is passed in the tag attributes. In that case, the values would be retrieved with the data.getAttributeString method.


public class IterationTEI extends TagExtraInfo {
  ...
  public VariableInfo[] getVariableInfo(TagData data) {
  VariableInfo[]  scriptVars = new VariableInfo[3];

  // We are telling the constructor  not to create
  // a new variable since we will define it in our JSP
  // loopCounter will be available in the body and
  // remainder of the JSP
scriptVars[0]  = new VariableInfo("loopCounter",
                            "java.lang.String",
                            false,
                            VariableInfo.AT_BEGIN);
	
 // loopCounter2  will be available after the tag 
 //and for the remainder of the JSP
scriptVars[1] = new VariableInfo("loopCounter2",
                            "java.lang.String",
                            true,
                            VariableInfo.AT_END);
	
// loopCounter3  will be available in the tag only
scriptVars[1] = new VariableInfo("loopCounter3",
                                 "java.lang.String",
                                 true,
                                 VariableInfo.NESTED);
          
 return scriptVars;
        }
     }

Instead of defining the entire Java source for this class for the tag handler (we've already done this for other handlers), I will just include the pertinent methods for this example.

public class IterationTag extends BodySupport{
…
define any attribute setter methods
…
public int doStartTag() throws JspException{
	// The TEI file defines loopCounter as 
      //AT_BEGIN and loopCounter3 as NESTED scope
      // so we define them here. They can also be 
      // defined in doInitBody and  modified/reset
      // in doAfterBody
	pageContext.setAttribute("loopCounter","0");
	pageContext.setAttribute("loopCounter3","3");

	return EVAL_BODY_TAG;
}
public int doEndTag() throws JspException{
	…
	do other processing
	…
// The TEI file defines loopCounter2 as AT_END 
// so we define it here
	pageContext.setAttribute("loopCounter2","2");
	return EVAL_BODY_TAG;
}
}

The JSP using these scripting variables would look like:

<%@ taglib uri="/oreillySample.tld" prefix="sample" %>
<%-- 
 We told the TEI not to create this variable
 so we are creating it here 
--%>
<% String loopCounter; %>

<sample:iteration >
<%-- loopCounter is available starting in the body --%>
The value of loopCounter1 = <% loopCounter %><br />
<%-- loopCounter3 is only available in the body --%>
The value of loopCounter3 = <% loopCounter3 %><br />
</sample:iteration>

<%-- loopCounter is still available  --%>
The value of loopCounter1 = <% loopCounter %><br />
<%-- loopCounter2 is available after the tag --%>
The value of loopCounter2 = <% loopCounter2 %><br />

Cooperating tags

Tags can also cooperate with each other by means of shared objects. In the following example, tag1 creates a named object, obj1, which is then reused by tag2. The convention encouraged by the JSP specification is that a tag with an attribute id creates and names an object, and the object is then referenced by other tags with an attribute named name.

<sample:tag1 id="obj1" attr1="value" />
<sample:tag2 name="obj1" />

This is also a useful feature when nesting tags since all objects created by the enclosing tag are available to all inner tags.

Conclusion

If you've gotten this far, then you should be ready to design and implement custom tag libraries, using all of their power in ways that work for your particular situation.

Sue Spielman is an associate editor for ONJava.com, covering JSP and Servlets technologies. She is also President and Senior Consulting Engineer for Switchback Software LLC.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.