FAQ
History
PreviousHomeNext Search
Feedback
Divider

Defining Tags

To define a tag, you need to:

This section describes the properties of TLDs and tag handlers and explains how to develop library descriptor elements and tag handlers for each type of tag introduced in the previous section.

Tag Library Descriptors

A tag library descriptor (TLD) is an XML document that describes a tag library. A TLD contains information about a library as a whole and about each tag contained in the library. TLDs are used by a Web container to validate the tags and by JSP page development tools.

When you create an tag library in the IDE, it creates a TLD file. You customize a tag library by right-clicking the TLD and choosing customize. The code generation properties do not appear in the TLD, but simply determine the location of generated tag handlers and the tag library JAR.The properties of the tag library are listed in Table 5-1:

Table 5-1 Tag Library Properties 
Property
Description
Version
The tag library's version
JSP Version
The JSP specification version that the tag library requires
Short name
Optional name that could be used by a JSP page authoring tool to create names with a mnemonic value
URI
A URI that uniquely identifies the tag library
Display name
Optional name intended to be displayed by tools
Small Icon
Optional small-icon that can be used by tools
Large Icon
Optional large-icon that can be used by tools
Description
Optional tag-specific information

listener Element

A tag library can specify some classes that are event listeners (see Handling Servlet Life Cycle Events). The listeners are listed in the TLD as listener elements, and the Web container will instantiate the listener classes and register them in a way analogous to listeners defined at the WAR level. Unlike WAR-level listeners, the order in which the tag library listeners are registered is undefined. The only subelement of the listener element is the listener-class element, which must contain the fully qualified name of the listener class. Since the IDE does not have a listener property on the customizer, you must manually add the listener element to the TLD as follows:

  1. Right-click the TLD.
  2. Choose open
  3. Uncomment the listener element at the bottom of the TLD.
  4. Replace the dummy listener class with your listener class.

Tag Properties

Each tag in the library is described by giving its name and the class of its tag handler, information on the scripting variables created by the tag, and information on the tag's attributes.

Scripting variable information can be given directly in the TLD or through a tag extra info class (see Tags That Define Scripting Variables). Each attribute declaration contains an indication of whether the attribute is required, whether its value can be determined by request-time expressions, and the type of the attribute (see Tag Attribute Properties).

Tag properties are listed in Table 5-2:

Table 5-2 Tag Properties
Property
Description
Name
The unique tag name
Tag Class
The fully-qualified name of the tag handler class
Display Name
Optional name intended to be displayed by tools
Small Icon
Optional small-icon that can be used by tools
Large Icon
Optional large-icon that can be used by tools
Description
Optional tag-specific information

To add a tag to a tag library using the IDE:

  1. Right-click the TLD file and choose Add Tag.
  2. Type the tag name and tag class name.
  3. Click OK.

The following sections describe the tag handler methods you need to develop and the properties you need to specify for each type of tag introduced in Types of Tags.

Tag Handlers

A tag handler is an object invoked by a Web container to evaluate a custom tag during the execution of the JSP page that references the tag. Tag handlers must implement either the Tag or BodyTag interface. Interfaces can be used to take an existing Java object and make it a tag handler. For newly created handlers, you can use the TagSupport and BodyTagSupport classes as base classes. These classes and interfaces are contained in the javax.servlet.jsp.tagext package.

Tag handler methods defined by the Tag and BodyTag interfaces are called by the JSP page's servlet at various points during the evaluation of the tag. When the start tag of a custom tag is encountered, the JSP page's servlet calls methods to initialize the appropriate handler and then invokes the handler's doStartTag method. When the end tag of a custom tag is encountered, the handler's doEndTag method is invoked. Additional methods are invoked in between when a tag handler needs to interact with the body of the tag. For further information, see Tags with Bodies. In order to provide a tag handler implementation, you must implement the methods, summarized in Table 5-3, that are invoked at various stages of processing the tag.

Table 5-3 Tag Handler Methods 
Tag Handler Type
Methods
Simple
doStartTag, doEndTag, release
Attributes
doStartTag, doEndTag, set/getAttribute1...N, release
Body, Evaluation and No Interaction
doStartTag, doEndTag, release
Body, Iterative Evaluation
doStartTag, doAfterBody, doEndTag, release
Body, Interaction
doStartTag, doEndTag, release, doInitBody, doAfterBody, release

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 page context object (javax.servlet.jsp.PageContext), through which a tag handler can retrieve all the other implicit objects (request, session, and application) accessible from a JSP page.

Implicit objects can have named attributes associated with them. Such attributes are accessed using [set|get]Attribute methods.

If the tag is nested, a tag handler also has access to the handler (called the parent) associated with the enclosing tag.

A set of related tag handler classes (a tag library) is usually packaged and deployed as a JAR archive.

You can generate a tag handler skeleton with the IDE as follows:

  1. Right-click the tag.
  2. Choose Customize.
  3. Select the Code Generation tab.
  4. Choose whether to implement an interface or extend a class which interface or class.
  5. Click OK.
  6. Right-click the tag and choose Generate Tag Handlers.

Once you have the skeleton, you must implement the tag handler methods as described in the following sections.

Simple Tags

Tag Handlers

The handler for a simple tag must implement the doStartTag and doEndTag methods of the Tag interface. The doStartTag method is invoked when the start tag is encountered. This method returns SKIP_BODY because a simple tag has no body. The doEndTag method is invoked when the end tag is encountered. The doEndTag method needs to return EVAL_PAGE if the rest of the page needs to be evaluated; otherwise, it should return SKIP_PAGE.

The simple tag discussed in the first section,

<tt:simple /> 

would be implemented by the following tag handler:

public SimpleTag extends TagSupport {
  public int doStartTag() throws JspException {
    try {
      pageContext.getOut().print("Hello.");
    } catch (Exception ex) {
      throw new JspTagException("SimpleTag: " + 
        ex.getMessage());
    }
    return SKIP_BODY;
  }
  public int doEndTag() {
    return EVAL_PAGE;
  }
} 

Tag Properties

To indicate that a tag has an empty body:

  1. Right-click the tag.
  2. Choose Customize.
  3. Choose empty from the Body Content drop-down list.
  4. Regenerate the tag handler.

Tags with Attributes

Defining Attributes in a Tag Handler

For each tag attribute, you must define a property and get and set methods that conform to the JavaBeans architecture conventions in the tag handler. For example, the tag handler for the Struts logic:present tag,

<logic:present parameter="Clear"> 

contains the following declaration and methods:

protected String parameter = null;
public String getParameter() {
  return (this.parameter);
}
public void setParameter(String parameter) {
  this.parameter = parameter;
} 

A tag attribute whose value is a String can name an attribute of one of the implicit objects available to tag handlers. An implicit object attribute would be accessed by passing the tag attribute value to the [set|get]Attribute method of the implicit object. This is a good way to pass scripting variable names to a tag handler where they are associated with objects stored in the page context (See Implicit Objects).

Tag Attribute Properties

For each tag attribute, you must specify whether the attribute is required, whether the value can be determined by an expression, and, optionally, the type of the attribute. For static values the type is always java.lang.String. If the value can be determined at run-time by an expression, they you must also specify the return type expected from any expression.

If a tag attribute is not required, a tag handler should provide a default value.

For example, to view the properties of the direct attribute of the template:parameter tag:

  1. In the IDE, mount the filesystem
    <INSTALL>/j2eetutorial/examples/web/template.
  2. Expand the template TLD node.
  3. Expand the parameter tag node.
  4. Expand the Attributes node.
  5. Right-click the direct attribute and choose Customize.

Notice how attribute is required, that its value can be set by a runtime expression, and that its type is boolean.

If you change a tag attribute, you must regenerate the tag handler.

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 with the isValid method of a class derived from TagExtraInfo. This class is also used to provide information about scripting variables defined by the tag (see Providing Information about the Scripting Variable).

The isValid method is passed the attribute information in a TagData object, which contains attribute-value tuples for each of the tag's attributes. Since 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.

Assume that the attribute attr1 of a tag is of type boolean and can be determined at runtime. The following isValid method checks that the value of attr1 is a valid Boolean value. Note that since the value of attr1 can be computed at runtime, isValid must check whether the tag user has chosen to provide a runtime value.

public class TwaTEI extends TagExtraInfo {
  public boolean isValid(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 true;
      else
        return false;
    }
    else
      return true;
  }
} 

Tags with Bodies

Tag Handlers

A tag handler for a tag with a body is implemented differently depending on whether the tag handler needs to interact with the body or not. By interact, we mean that the tag handler reads or modifies the contents of the body.

Tag Handler Does Not Interact with the Body

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 needs to return EVAL_BODY_INCLUDE; otherwise it should return SKIP_BODY.

If a tag handler needs to iteratively evaluate the body, it should implement the IterationTag interface or be derived from TagSupport. It should return EVAL_BODY_AGAIN from the doStartTag and doAfterBody methods if it determines that the body needs to be evaluated again.

Tag Handler Interacts with the 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 page's servlet.

Body content supports several methods to read and write its contents. A tag handler can use the body content's getString or getReader methods to extract information from the body, and the writeOut(out) method to write the body contents to an out stream. The writer supplied to the writeOut method is obtained using the tag handler's getPreviousOut method. This method is used to ensure that a tag handler's results are available to an enclosing tag handler.

If the body of the tag needs to be evaluated, the doStartTag method needs to return EVAL_BODY_BUFFERED; otherwise, it should return SKIP_BODY.

doInitBody Method

The doInitBody method is called after the body content is set but before it is evaluated. You generally use this method to perform any initialization that depends on the body content.

doAfterBody Method

The doAfterBody method is called after the body content is evaluated.

Like the doStartTag method, doAfterBody must return an indication of whether to continue evaluating the body. Thus, if the body should be evaluated again, as would be the case if you were implementing an iteration tag, doAfterBody should return EVAL_BODY_BUFFERED; otherwise, doAfterBody should return SKIP_BODY.

release Method

A tag handler should reset its state and release any private resources in the release method.

The following example reads the content of the body (which contains a SQL query) and passes it to an object that executes the query. Since the body does not need to be reevaluated, doAfterBody returns SKIP_BODY.

public class QueryTag extends BodyTagSupport {
  public int doAfterBody() throws JspTagException {
    BodyContent bc = getBodyContent();
    // get the bc as string
    String query = bc.getString();
    // clean up
    bc.clearBody();
    try {
      Statement stmt = connection.createStatement();
      result = stmt.executeQuery(query);
    } catch (SQLException e) {
      throw new JspTagException("QueryTag: " +
         e.getMessage());
    }
    return SKIP_BODY;
  }
} 

Tag Properties

Body content containing custom and core tags, scripting elements, and HTML text is categorized as JSP. This is the value declared for the Struts logic:present tag. All other types of body content--for example--SQL statements passed to the query tag, would be labeled tagdependent.

To indicate that a tag has a body:

  1. Right-click the tag.
  2. Choose Customize.
  3. Choose the content type from the Body Content drop-down list.
  4. Regenerate the tag handler.

Tags That Define Scripting Variables

Tag Handlers

A tag handler is responsible for creating and setting the object referred to by the scripting variable into a context accessible from the page. It does this by using the pageContext.setAttribute(name, value, scope) or pageContext.setAttribute(name, value) methods. Typically an attribute passed to the custom tag specifies the name of the scripting variable object; this name can be retrieved by invoking the attribute's get method described in Using Scope Objects.

If the value of the scripting variable is dependent on an object present in the tag handler's context, it can retrieve the object using the pageContext.getAttribute(name, scope) method.

The usual procedure is that the tag handler retrieves a scripting variable, performs some processing on the object, and then sets the scripting variable's value using the pageContext.setAttribute(name, object) method.

The scope that an object can have is summarized in Table 5-4. The scope constrains the accessibility and lifetime of the object.

Table 5-4 Scope of Objects 
Name
Accessible From
Lifetime
page
Current page
Until the response has been sent back to the user or the request is passed to a new page
request
Current page and any included or forwarded pages
Until the response has been sent back to the user
session
Current request and any subsequent request from the same browser (subject to session lifetime)
The life of the user's session
application
Current and any future request from the same Web application
The life of the application

Providing Information about the Scripting Variable

The last example described in Tags That Define Scripting Variables defines a scripting variable name that is used for accessing information about the elements in a collection:

<tlt:iterator name="member" type="org.Member" 
  group="<%= dept.getMembers()%>">
  <tr>
    <td><jsp:getProperty name="member" 
      property="name"/></td>
    <td><jsp:getProperty name="member"
      property="phone"/></td>
    <td><jsp:getProperty name="member"
      property="email"/></td>
  </tr>
</tlt:iterator> 

When the JSP page containing this tag is translated, the Web container generates code to synchronize the scripting variable with the object referenced by the variable. To generate the code, the Web container requires certain information about the scripting variable:

There are two ways to provide this information: by specifying scripting variable properties in the TLD or by defining a tag extra info class and including the tei-class element in the TLD. Using the variable element is simpler, but slightly less flexible.

Scripting Variable Properties

A scripting variable has a names. The name can be specified either as a constant or via the name of an attribute whose translation-time value specifies the name of the variable.

The following scripting variable properties are optional:

TagExtraInfo Class

You can also define a tag extra info class by extending the class javax.servlet.jsp.TagExtraInfo. A TagExtraInfo must implement the getVariableInfo method to return an array of VariableInfo objects containing the following information:

The Web container passes a parameter called data to the getVariableInfo method that contains attribute-value tuples for each of the tag's attributes. These attributes can be used to provide the VariableInfo object with a scripting variable's name and class.

The iterator tag library provides information about the scripting variable created by the iterator tag in the IteratorTEI tag extra info class because the scripting variable customizer does not allow a tag attribute to specify the type of the variable. Since the name (member) and class (org.Member) of the scripting variable are passed in as tag attributes, they are be retrieved with the data.getAttributeString method and used to fill in the VariableInfo constructor. To allow the scripting variable name to be used within the scope of the tag, the scope of book is set to be NESTED.

public class IteratorTEI extends TagExtraInfo {
  ...
    public VariableInfo[] getVariableInfo(TagData data) {
    VariableInfo info1 = new VariableInfo(
      data.getAttributeString("name"),
      data.getAttributeString("type"),  
      true,
        VariableInfo.NESTED);
      VariableInfo [] info = { info1 };
    return info;
  }
} 

Since the tag library customizer does not have a TEI class property, the fully qualified name of the tag extra info class defined for a scripting variable must be manually added to the TLD in the tei-class subelement of the tag element. 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 both JSP pages and 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 with the static method TagSupport.findAncestorWithClass(from, class) or the TagSupport.getParent method. The former method should be used when a specific nesting of tag handlers cannot be guaranteed. Once 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 objects can be stored in a tag handler with the setValue method and retrieved with the getValue method.

The following example illustrates a tag handler that supports both the named and private object approaches to sharing objects. In the example, the handler for a query tag checks whether an attribute named connection has been set in the doStartTag method. If the connection 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 BodyTagSupport {
  private String connectionId;
  public int doStartTag() throws JspException {
    String cid = getConnection();
    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 could be used in either of the following ways:

<tt:connection id="con01" ....> ... </tt:connection>
<tt:query id="balances" connection="con01"> 
  SELECT account, balance FROM acct_table 
    where customer_number = <%= request.getCustno()%> 
</tt:query>

<tt:connection ...>
  <x:query id="balances"> 
    SELECT account, balance FROM acct_table 
      where customer_number = <%= request.getCustno()%> 
  </x:query>
</tt:connection> 

The TLD for the tag handler must indicate that the connection attribute is optional with the following declaration:

<tag>
  ...
  <attribute>
    <name>connection</name>
    <required>false</required>
  </attribute>
</tag> 
Divider
FAQ
History
PreviousHomeNext Search
Feedback
Divider

All of the material in The J2EE Tutorial for the Sun ONE Platform is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.