Programming Simple Tag Handlers

The classes and interfaces used to implement simple tag handlers are contained in the javax.servlet.jsp.tagext package. Simple tag handlers implement the SimpleTag interface. Interfaces can be used to take an existing Java object and make it a tag handler. For most newly created handlers, you would use the SimpleTagSupport classes as a base class.

The heart of a simple tag handler is a single methoddoTagwhich gets invoked when the end element of the tag is encountered. Note that the default implementation of the doTag method of SimpleTagSupport does nothing.

A tag handler has access to an API that allows it to communicate with the JSP page. The entry point to the API is the JSP context object (javax.servlet.jsp.JspContext). The JspContext object provides access to implicit objects. PageContext extends JspContext with servlet-specific behavior. Through these objects, a tag handler can retrieve all the other implicit objects (request, session, and application) that are accessible from a JSP page. If the tag is nested, a tag handler also has access to the handler (called the parent) that is associated with the enclosing tag.

Including Tag Handlers in Web Applications

Tag handlers can be made available to a web application in two basic ways. The classes implementing the tag handlers can be stored in an unpacked form in the WEB-INF/classes/ subdirectory of the web application. Alternatively, if the library is distributed as a JAR, it is stored in the WEB-INF/lib/ directory of the web application.

How Is a Simple Tag Handler Invoked?

The SimpleTag interface defines the basic protocol between a simple tag handler and a JSP page's servlet. The JSP page's servlet invokes the setJspContext, setParent, and attribute setting methods before calling doStartTag.

   ATag t = new ATag();    t.setJSPContext(...);    t.setParent(...);    t.setAttribute1(value1);    t.setAttribute2(value2);    ...    t.setJspBody(new JspFragment(...))    t.doTag();

The following sections describe the methods that you need to develop for each type of tag introduced in Types of Tags (page 207).

Tag Handlers for Basic Tags

The handler for a basic tag without a body must implement the doTag method of the SimpleTag interface. The doTag method is invoked when the end element of the tag is encountered.

The basic tag discussed in the first section, <tt:basic />, would be implemented by the following tag handler:

   public HelloWorldSimpleTag extends SimpleTagSupport {      public void doTag() throws JspException, IOException {        getJspContext().getOut().write("Hello, world.");      }    }

Tag Handlers for Tags with Attributes

This section describes how to define attributes for a tag handler and how to validate attribute values.

Defining Attributes in a Tag Handler

For each tag attribute, you must define a set method in the tag handler that conforms to the JavaBeans architecture conventions. For example, consider the tag handler for the JSTL c:if tag:

   <c:if test="${Clear}">

This tag handler contains the following method:

   public void setTest(boolean test) {      this.test = test;    }

As shown by the preceding example, the name of the attribute must match the name of the set method.

Attribute Validation

The documentation for a tag library should describe valid values for tag attributes. When a JSP page is translated, a web container will enforce any constraints contained in the TLD element for each attribute.

The attributes passed to a tag can also be validated at translation time using the validate method of a class derived from TagExtraInfo. This class is also used to provide information about variables defined by the tag (see TagExtraInfo Class, page 248).

The validate method is passed the attribute information in a TagData object, which contains attribute-value tuples for each of the tag's attributes. Because the validation occurs at translation time, the value of an attribute that is computed at request time will be set to TagData.REQUEST_TIME_VALUE.

The tag <tt:twa attr1="value1"/> has the following TLD attribute element:

   <attribute>      <name>attr1</name>      <required>true</required>      <rtexprvalue>true</rtexprvalue>    </attribute>

This declaration indicates that the value of attr1 can be determined at runtime.

The following validate method checks whether the value of attr1 is a valid Boolean value. Note that because the value of attr1 can be computed at runtime, validate must check whether the tag user has chosen to provide a runtime value.

   public class TwaTEI extends TagExtraInfo {      public ValidationMessage[] validate(TagData data) {        Object o = data.getAttribute("attr1");        if (o != null && o != TagData.REQUEST_TIME_VALUE) {          if (((String)o).toLowerCase().equals("true") ||            ((String)o).toLowerCase().equals("false") )            return null;          else            return new ValidationMessage(data.getId(),              "Invalid boolean value.");        }        else          return null;      }    }

Setting Dynamic Attributes

Simple tag handlers that support dynamic attributes must declare that they do so in the tag element of the TLD (see Declaring Tag Handlers, page 234). In addition, your tag handler must implement the setDynamicAttribute method of the DynamicAttributes interface. For each attribute specified in the tag invocation that does not have a corresponding attribute element in the TLD, the web container calls setDynamicAttribute, passing in the namespace of the attribute (or null if in the default namespace), the name of the attribute, and the value of the attribute. You must implement the setDynamicAttribute method to remember the names and values of the dynamic attributes so that they can be used later when doTag is executed. If the setDynamicAttribute method throws an exception, the doTag method is not invoked for the tag, and the exception must be treated in the same manner as if it came from an attribute setter method.

The following implementation of setDynamicAttribute saves the attribute names and values in lists. Then, in the doTag method, the names and values are echoed to the response in an HTML list.

   private ArrayList keys = new ArrayList();    private ArrayList values = new ArrayList();    public void setDynamicAttribute(String uri,      String localName, Object value ) throws JspException {      keys.add( localName );      values.add( value );    }    public void doTag() throws JspException, IOException {      JspWriter out = getJspContext().getOut();      for( int i = 0; i < keys.size(); i++ ) {          String key = (String)keys.get( i );          Object value = values.get( i );          out.println( "<li>" + key + " = " + value + "</li>" );      }   }

Setting Deferred Value Attributes and Deferred Method Attributes

For each tag attribute that accepts a deferred value expression or a deferred method expression, the tag handler must have a method to access the value of the attribute.

The methods that access the value of a deferred value attribute method must accept a ValueExpression object. The methods that access the value of a deferred method attribute must accept a MethodExpression object. These methods take the form setX, where X is the name of the attribute.

The following example shows a method that can be used to access the value of a deferred value attribute called attributeName:

   private javax.el.ValueExpression attributeName = null;    public void setAttributeName(      javax.el.ValueExpression attributeName)    {      this.immediate = immediate;    }

Deferred value attributes and deferred method attributes are primarily used by JavaServer Faces technology. See Getting the Attribute Values (page 451) for an example of creating a tag handler that processes these attributes for a JavaServer Faces application.

If you have an attribute that is both dynamic and deferred (meaning that the tag attribute definition accepts a deferred expression and has rtexprvalue set to true), then the setX method that accesses this value must accept an Object instance and test if the Object instance is a deferred value expression, as shown in this pseudocode:

   public void setAttr(Object obj) {      if (obj instance of ValueExpression) {        // this is a deferred expression      else {        // this is an rtexpression      }    }

Tag Handlers for Tags with Bodies

A simple tag handler for a tag with a body is implemented differently depending on whether or not the tag handler needs to manipulate the body. A tag handler manipulates the body when it reads or modifies the contents of the body.

Tag Handler Does Not Manipulate the Body

If a tag handler needs simply to evaluate the body, it gets the body using the getJspBody method of SimpleTag and then evaluates the body using the invoke method.

The following tag handler accepts a test parameter and evaluates the body of the tag if the test evaluates to TRue. The body of the tag is encapsulated in a JSP fragment. If the test is TRue, the handler retrieves the fragment using the getJspBody method. The invoke method directs all output to a supplied writer or, if the writer is null, to the JspWriter returned by the getOut method of the JspContext associated with the tag handler.

   public class IfSimpleTag extends SimpleTagSupport {      private boolean test;      public void setTest(boolean test) {        this.test = test;      }      public void doTag() throws JspException, IOException {        if(test){          getJspBody().invoke(null);        }      }    }

Tag Handler Manipulates the Body

If the tag handler needs to manipulate the body, the tag handler must capture the body in a StringWriter. The invoke method directs all output to a supplied writer. Then the modified body is written to the JspWriter returned by the getOut method of the JspContext. Thus, a tag that converts its body to uppercase could be written as follows:

   public class SimpleWriter extends SimpleTagSupport {      public void doTag() throws JspException, IOException {         StringWriter sw = new StringWriter();         jspBody.invoke(sw);         jspContext().           getOut().println(sw.toString().toUpperCase());      }    }

Tag Handlers for Tags That Define Variables

Similar communication mechanisms exist for communication between JSP page and tag handlers as for JSP pages and tag files.

To emulate IN parameters, use tag attributes. A tag attribute is communicated between the calling page and the tag handler when the tag is invoked. No further communication occurs between the calling page and the tag handler.

To emulate OUT or nested parameters, use variables with availability AT_BEGIN, AT_END, or NESTED. The variable is not initialized by the calling page but instead is set by the tag handler.

For AT_BEGIN availability, the variable is available in the calling page from the start tag until the scope of any enclosing tag. If there's no enclosing tag, then the variable is available to the end of the page. For AT_END availability, the variable is available in the calling page after the end tag until the scope of any enclosing tag. If there's no enclosing tag, then the variable is available to the end of the page. For nested parameters, the variable is available in the calling page between the start tag and the end tag.

When you develop a tag handler you are responsible for creating and setting the object referenced by the variable into a context that is accessible from the page. You do this by using the JspContext().setAttribute(name, value) or JspContext.setAttribute(name, value, scope) method. You retrieve the page context using the getJspContext method of SimpleTag.

Typically, an attribute passed to the custom tag specifies the name of the variable and the value of the variable is dependent on another attribute. For example, the iterator tag introduced in Chapter 4 retrieves the name of the variable from the var attribute and determines the value of the variable from a computation performed on the group attribute.

   public void doTag() throws JspException, IOException {      if (iterator == null)        return;      while (iterator.hasNext()) {        getJspContext().setAttribute(var,;        getJspBody().invoke(null);      }    }    public void setVar(String var) {      this.var = var;    }    public void setGroup(Collection group) { = group;      if(group.size() > 0)        iterator = group.iterator();    }

The scope that a variable can have is summarized in Table 713. The scope constrains the accessibility and lifetime of the object.

Table 713. Scope of Objects


Accessible From



Current page

Until the response has been sent back to the user or the request is passed to a new page


Current page and any included or forwarded pages

Until the response has been sent back to the user


Current request and any subsequent request from the same browser (subject to session lifetime)

The life of the user's session


Current and any future request in the same web application

The life of the application

TagExtraInfo Class

In Declaring Tag Variables for Tag Handlers (page 238) we discussed how to provide information about tag variables in the tag library descriptor. Here we describe another approach: defining a tag extra info class. You define a tag extra info class by extending the class javax.servlet.jsp.tagext.TagExtraInfo. A TagExtraInfo must implement the getVariableInfo method to return an array of VariableInfo objects containing the following information:

  • Variable name

  • Variable class

  • Whether the variable refers to a new object

  • The availability of the variable

The web container passes a parameter of type javax.servlet.jsp.tagext.TagData to the getVariableInfo method, which contains attribute-value tuples for each of the tag's attributes. These attributes can be used to provide the VariableInfo object with an EL variable's name and class.

The following example demonstrates how to provide information about the variable created by the iterator tag in a tag extra info class. Because the name (var) and class (type) of the variable are passed in as tag attributes, they can be retrieved using the data.getAttributeString method and can be used to fill in the VariableInfo constructor. To allow the variable var to be used only within the tag body, you set the scope of the object to NESTED.

   package iterator;    public class IteratorTEI extends TagExtraInfo {      public VariableInfo[] getVariableInfo(TagData data) {        String type = data.getAttributeString("type");        if (type == null)          type = "java.lang.Object";        return new VariableInfo[] {          new VariableInfo(data.getAttributeString("var"),          type,          true,          VariableInfo.NESTED)        };      }    }

The fully qualified name of the tag extra info class defined for an EL variable must be declared in the TLD in the tei-class subelement of the tag element. Thus, the tei-class element for IteratorTei would be as follows:

   <tei-class>      iterator.IteratorTEI    </tei-class>

Cooperating Tags

Tags cooperate by sharing objects. JSP technology supports two styles of object sharing.

The first style requires that a shared object be named and stored in the page context (one of the implicit objects accessible to JSP pages as well as tag handlers). To access objects created and named by another tag, a tag handler uses the pageContext.getAttribute(name, scope) method.

In the second style of object sharing, an object created by the enclosing tag handler of a group of nested tags is available to all inner tag handlers. This form of object sharing has the advantage that it uses a private namespace for the objects, thus reducing the potential for naming conflicts.

To access an object created by an enclosing tag, a tag handler must first obtain its enclosing tag by using the static method SimpleTagSupport.findAncestorWithClass(from, class) or the SimpleTagSupport.getParent method. The former method should be used when a specific nesting of tag handlers cannot be guaranteed. After the ancestor has been retrieved, a tag handler can access any statically or dynamically created objects. Statically created objects are members of the parent. Private objects can also be created dynamically. Such privately named objects would have to be managed by the tag handler; one approach would be to use a Map to store name-object pairs.

The following example illustrates a tag handler that supports both the named approach and the private object approach to sharing objects. In the example, the handler for a query tag checks whether an attribute named connectionId has been set. If the connectionId attribute has been set, the handler retrieves the connection object from the page context. Otherwise, the tag handler first retrieves the tag handler for the enclosing tag and then retrieves the connection object from that handler.

   public class QueryTag extends SimpleTagSupport {      public int doTag() throws JspException {        String cid = getConnectionId();        Connection connection;        if (cid != null) {        // there is a connection id, use it          connection =(Connection)pageContext.            getAttribute(cid);        } else {          ConnectionTag ancestorTag =            (ConnectionTag)findAncestorWithClass(this,               ConnectionTag.class);          if (ancestorTag == null) {            throw new JspTagException("A query without              a connection attribute must be nested              within a connection tag.");          }          connection = ancestorTag.getConnection();          ...        }      }    }

The query tag implemented by this tag handler can be used in either of the following ways:

   <tt:connection c ... >      ...    </tt:connection>    <tt:query  connection>      SELECT account, balance FROM acct_table        where customer_number = ?      <tt:param value="${requestScope.custNumber}" />    </tt:query>    <tt:connection ... >      <tt:query c>        SELECT account, balance FROM acct_table        where customer_number = ?        <tt:param value="${requestScope.custNumber}" />      </tt:query>    </tt:connection>

The TLD for the tag handler uses the following declaration to indicate that the connectionId attribute is optional:

   <tag>      ...      <attribute>        <name>connectionId</name>        <required>false</required>      </attribute>    </tag>


The simple tags described in this section demonstrate solutions to two recurring problems in developing JSP applications: minimizing the amount of Java programming in JSP pages and ensuring a common look and feel across applications. In doing so, they illustrate many of the styles of tags discussed in the first part of the chapter.

An Iteration Tag

Constructing page content that is dependent on dynamically generated data often requires the use of flow control scripting statements. By moving the flow control logic to tag handlers, flow control tags reduce the amount of scripting needed in JSP pages. Iteration is a very common flow control function and is easily handled by a custom tag.

The discussion on using tag libraries in Chapter 4 introduced a tag library containing an iterator tag. The tag retrieves objects from a collection stored in a JavaBeans component and assigns them to an EL variable. The body of the tag retrieves information from the variable. As long as elements remain in the collection, the iterator tag causes the body to be reevaluated. The tag in this example is simplified to make it easy to demonstrate how to program a custom tag. web applications requiring such functionality should use the JSTL forEach tag, which is discussed in Iterator Tags (page 184).

JSP Page

The index.jsp page invokes the iterator tag to iterate through a collection of department names. Each item in the collection is assigned to the departmentName variable.

   <%@ taglib uri="/tlt" prefix="tlt" %>    <html>      <head>      <title>Departments</title>      </head>      <body bgcolor="white">      <jsp:useBean  />      <table border=2 cellspacing=3 cellpadding=3>        <tr>           <td><b>Departments</b></td>        </tr>      <tlt:iterator var="departmentName" type="java.lang.String"           group="${myorg.departmentNames}">        <tr>           <td><a href="list.jsp?deptName=${departmentName}">              ${departmentName}</a></td>        </tr>     </tlt:iterator>     </table>     </body>    </html>

Tag Handler

The collection is set in the tag handler via the group attribute. The tag handler retrieves an element from the group and passes the element back to the page in the EL variable whose name is determined by the var attribute. The variable is accessed in the calling page using the JSP expression language. After the variable is set, the tag body is evaluated with the invoke method.

   public void doTag() throws JspException, IOException {      if (iterator == null)        return;      while (iterator.hasNext()) {        getJspContext().setAttribute(var,;        getJspBody().invoke(null);      }    }    public void setVar(String var) {      this.var = var;    }    public void setGroup(Collection group) { = group;      if(group.size() > 0)        iterator = group.iterator();    }

A Template Tag Library

A template provides a way to separate the common elements that are part of each screen from the elements that change with each screen of an application. Putting all the common elements together into one file makes it easier to maintain and enforce a consistent look and feel in all the screens. It also makes development of individual screens easier because the designer can focus on portions of a screen that are specific to that screen while the template takes care of the common portions.

The template is a JSP page that has placeholders for the parts that need to change with each screen. Each of these placeholders is referred to as a parameter of the template. For example, a simple template might include a title parameter for the top of the generated screen and a body parameter to refer to a JSP page for the custom content of the screen.

The template uses a set of nested tagsdefinition, screen, and parameterto define a table of screen definitions and uses an insert tag to insert parameters from a screen definition into a specific application screen.

JSP Pages

The template for the Duke's Bookstore example, template.jsp, is shown next. This page includes a JSP page that creates the screen definition and then uses the insert tag to insert parameters from the definition into the application screen.

   <%@ taglib uri="/tutorial-template" prefix="tt" %>    <%@ page errorPage="/template/errorinclude.jsp" %>    <%@ include file="/template/screendefinitions.jsp" %>    <html>    <head>    <title>    <tt:insert definition="bookstore" parameter="title"/>    </title>    </head>    <body  bgcolor="#FFFFFF">      <tt:insert definition="bookstore" parameter="banner"/>    <tt:insert definition="bookstore" parameter="body"/>    <center><em>Copyright &copy; 2004 Sun Microsystems, Inc. </    em></center>    </body>    </html>

The screendefinitions.jsp page creates a definition for the screen specified by the request attribute javax.servlet.forward.servlet_path:

   <tt:definition name="bookstore"    screen="${requestScope      ['javax.servlet.forward.servlet_path']}">      <tt:screen >      <tt:parameter name="title" value="Duke's Bookstore"         direct="true"/>      <tt:parameter name="banner" value="/template/banner.jsp"         direct="false"/>      <tt:parameter name="body" value="/bookstore.jsp"         direct="false"/>      </tt:screen>      <tt:screen >      <tt:parameter name="title" direct="true">          <jsp:attribute name="value" >            <fmt:message key="TitleBookCatalog"/>          </jsp:attribute>        </tt:parameter>        <tt:parameter name="banner" value="/template/banner.jsp"         direct="false"/>         <tt:parameter name="body" value="/bookcatalog.jsp"         direct="false"/>      </tt:screen>      ...    </tt:definition>

The template is instantiated by the Dispatcher servlet. Dispatcher first gets the requested screen. Dispatcher performs business logic and updates model objects based on the requested screen. For example, if the requested screen is /bookcatalog, Dispatcher determines whether a book is being added to the cart based on the value of the Add request parameter. It sets the price of the book if it's on sale, and then adds the book to the cart. Finally, the servlet dispatches the request to template.jsp:

   public class Dispatcher extends HttpServlet {      @Resource      UserTransaction utx;      public void doGet(HttpServletRequest request,        HttpServletResponse response) {        String bookId = null;        Book book = null;        String clear = null;        BookDBAO bookDBAO =           (BookDBAO)getServletContext().             getAttribute("bookDBAO");        HttpSession session = request.getSession();        String selectedScreen = request.getServletPath();        ShoppingCart cart = (ShoppingCart)session.           getAttribute("cart");        if (cart == null) {           cart = new ShoppingCart();           session.setAttribute("cart", cart);        }        if (selectedScreen.equals("/bookcatalog")) {           bookId = request.getParameter("Add");           if (!bookId.equals("")) {             try {                book = bookDBAO.getBook(bookId);                if ( book.getOnSale() ) {                  double sale = book.getPrice() * .85;                  Float salePrice = new Float(sale);                  book.setPrice(salePrice.floatValue());                }                cart.add(bookId, book);             } catch (BookNotFoundException ex) {               // not possible             }          }        } else if (selectedScreen.equals("/bookshowcart")) {           bookId =request.getParameter("Remove");           if (bookId != null) {             cart.remove(bookId);           }           clear = request.getParameter("Clear");           if (clear != null && clear.equals("clear")) {             cart.clear();           }         } else if (selectedScreen.equals("/bookreceipt")) {         // Update the inventory            try {               utx.begin();               bookDBAO.buyBooks(cart);               utx.commit();            } catch (Exception ex) {              try {                 utx.rollback();                 request.getRequestDispatcher(                   "/bookordererror.jsp").                   forward(request, response);              } catch(Exception e) {                   System.out.println(                     "Rollback failed: "+e.getMessage());                   e.printStackTrace();              }           }              }              try {                 request.                   getRequestDispatcher(                   "/template/template.jsp").                   forward(request, response);              } catch(Exception ex) {                 ex.printStackTrace();              }          }          public void doPost(HttpServletRequest request,            HttpServletResponse response) {            request.setAttribute("selectedScreen",              request.getServletPath());            try {               request.                 getRequestDispatcher(                 "/template/template.jsp").                 forward(request, response);            } catch(Exception ex) {               ex.printStackTrace();            }         }      }

Tag Handlers

The template tag library contains four tag handlersDefinitionTag, ScreenTag, ParameterTag, and InsertTagthat demonstrate the use of cooperating tags. DefinitionTag, ScreenTag, and ParameterTag constitute a set of nested tag handlers that share private objects. DefinitionTag creates a public object named bookstore that is used by InsertTag.

In doTag, DefinitionTag creates a private object named screens that contains a hash table of screen definitions. A screen definition consists of a screen identifier and a set of parameters associated with the screen. These parameters are loaded when the body of the definition tag, which contains nested screen and parameter tags, is invoked. DefinitionTag creates a public object of class Definition, selects a screen definition from the screens object based on the URL passed in the request, and uses this screen definition to initialize a public Definition object.

   public int doTag() {      try {         screens = new HashMap();         getJspBody().invoke(null);         Definition definition = new Definition();         PageContext context = (PageContext)getJspContext();         ArrayList params = (ArrayList) screens.get(screenId);         Iterator ir = null;         if (params != null) {           ir = params.iterator();           while (ir.hasNext())              definition.setParam((Parameter);         // put the definition in the page context         context.setAttribute(definitionName, definition,            context.APPLICATION_SCOPE);         }      }

The table of screen definitions is filled in by ScreenTag and ParameterTag from text provided as attributes to these tags. Table 714 shows the contents of the screen definitions hash table for the Duke's Bookstore application.

Table 714. Screen Definitions

Screen ID





Duke's Bookstore




Book Catalog




Book Description




Shopping Cart











If the URL passed in the request is /bookstore, the Definition object contains the items from the first row of Table 714 (see Table 715).

Table 715. Definition Object Contents for URL /bookstore




Duke's Bookstore



The parameters for the URL /bookstore are shown in Table 716. The parameters specify that the value of the title parameter, Duke's Bookstore, should be inserted directly into the output stream, but the values of banner and body should be included dynamically.

Table 716. Parameters for the URL /bookstore

Parameter Name

Parameter Value



Duke's Bookstore








InsertTag inserts parameters of the screen definition into the response. The doTag method retrieves the definition object from the page context and then inserts the parameter value. If the parameter is direct, it is directly inserted into the response; otherwise, the request is sent to the parameter, and the response is dynamically included into the overall response.

   public void doTag() throws JspTagException {      Definition definition = null;      Parameter parameter = null;      boolean directInclude = false;      PageContext context = (PageContext)getJspContext();         // get the definition from the page context      definition = (Definition)context.getAttribute(         definitionName, context.APPLICATION_SCOPE);         // get the parameter       if (parameterName != null && definition != null)         parameter = (Parameter)            definition.getParam(parameterName);       if (parameter != null)          directInclude = parameter.isDirect();       try {          // if parameter is direct, print to out          if (directInclude && parameter  != null)            context.getOut().print(parameter.getValue());          // if parameter is indirect,               include results of dispatching to page          else {            if ((parameter != null) &&              (parameter.getValue() !=  null))            context.include(parameter.getValue());          }       } catch (Exception ex) {           throw new JspTagException(ex.getMessage());       }     }

The JavaT EE 5 Tutorial
The JavaT EE 5 Tutorial
Year: 2004
Pages: 309 © 2008-2017.
If you may any questions please contact us: