The Java EE 5 Tutorial

Chapter 8 Custom Tags in JSP Pages

The standard JSP tags simplify JSP page development and maintenance. JSP technology also provides a mechanism for encapsulating other types of dynamic functionality in custom tags, which are extensions to the JSP language. Some examples of tasks that can be performed by custom tags include operating on implicit objects, processing forms, accessing databases and other enterprise services such as email and directories, and implementing flow control. Custom tags increase productivity because they can be reused in more than one application.

Custom tags are distributed in a tag library, which defines a set of related custom tags and contains the objects that implement the tags. The object that implements a custom tag is called a tag handler. JSP technology defines two types of tag handlers: simple and classic. Simple tag handlers can be used only for tags that do not use scripting elements in attribute values or the tag body. Classic tag handlers must be used if scripting elements are required. Simple tag handlers are covered in this chapter, and classic tag handlers are discussed in Chapter 9, Scripting in JSP Pages.

You can write simple tag handlers using the JSP language or using the Java language. A tag file is a source file containing a reusable fragment of JSP code that is translated into a simple tag handler by the web container. Tag files can be used to develop custom tags that are presentation-centric or that can take advantage of existing tag libraries, or by page authors who do not know Java. When the flexibility of the Java programming language is needed to define the tag, JSP technology provides a simple API for developing a tag handler in the Java programming language.

This chapter assumes that you are familiar with the material in Chapter 5, JavaServer Pages Technology, especially the section Using Custom Tags. For more information about tag libraries and for pointers to some freely available libraries, see http://java.sun.com/products/jsp/taglibraries/index.jsp.

What Is a Custom Tag?

A custom tag is a user-defined JSP language element. When a JSP page containing a custom tag is translated into a servlet, the tag is converted to operations on a tag handler. The web container then invokes those operations when the JSP page’s servlet is executed.

Custom tags have a rich set of features. They can

The Example JSP Pages

This chapter describes the tasks involved in defining simple tags. It illustrates the tasks using excerpts from the JSP version of the Duke’s Bookstore application discussed in The Example JSP Pages, rewritten here to take advantage of several custom tags:

The tutorial-template tag library defines a set of tags for creating an application template. 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 is created using a set of nested tags (definition, screen, and parameter) that are used to build a table of screen definitions for Duke’s Bookstore. An insert tag to insert parameters from the table into the screen.

Figure 8–1 shows the flow of a request through the following Duke’s Bookstore web components:

Figure 8–1 Request Flow through Duke’s Bookstore Components

Diagram shows how a request is routed through a servlet
and two JSP pages to the Book DB, then a response is routed through a JSP
page to the client.

The source code for the Duke’s Bookstore application is located in the tut-install/javaeetutorial5/examples/web/bookstore3/ directory created when you unzip the tutorial bundle (see Chapter 2, Using the Tutorial Examples).

    To deploy and run the application using NetBeans IDE, follow these steps:

  1. Perform all the operations described in Accessing Databases from Web Applications.

  2. In NetBeans IDE, select File->Open Project.

  3. In the Open Project dialog, navigate to:


    tut-install/javaeetutorial5/examples/web/
  4. Select the bookstore3 folder.

  5. Select the Open as Main Project check box and the Open Required Projects check box.

  6. Click Open Project.

  7. In the Projects tab, right-click the bookstore3 project, and select Undeploy and Deploy.

  8. To run the application, open the bookstore URL http://localhost:8080/bookstore3/bookstore.

    To deploy and run the application using Ant, follow these steps:

  1. In a terminal window, go to tut-install/javaeetutorial5/examples/web/bookstore3/.

  2. Type ant. This command will spawn any necessary compilations, copy files to the tut-install/javaeetutorial5/examples/web/bookstore3/build/ directory, and create a WAR file and copy it to the tut-install/javaeetutorial5/examples/web/bookstore3/dist/ directory.

  3. Start the Application Server.

  4. Perform all the operations described in Creating a Data Source in the Application Server.

  5. To deploy the example, type ant deploy. The deploy target outputs a URL for running the application. Ignore this URL, and instead use the one shown in the next step.

  6. To run the application, open the bookstore URL http://localhost:8080/bookstore3/bookstore.

To learn how to configure the example, refer to the web.xml file, which includes the following configurations:

To run the example, open the bookstore URL http://localhost:8080/bookstore3/bookstore.

See Troubleshooting Duke's Bookstore Database Problems for help with diagnosing common problems.

Types of Tags

Simple tags are invoked using XML syntax. They have a start tag and an end tag, and possibly a body:

<tt:tag>
    body</tt:tag>

A custom tag with no body is expressed as follows:

<tt:tag /> or <tt:tag></tt:tag>

Tags with Attributes

A simple tag can have attributes. Attributes customize the behavior of a custom tag just as parameters customize the behavior of a method. There are three types of attributes:

Simple Attributes

Simple attributes are evaluated by the container before being passed to the tag handler. Simple attributes are listed in the start tag and have the syntax attr="value". You can set a simple attribute value from a String constant, or an expression language (EL) expression, or by using a jsp:attribute element (see jsp:attribute Element). The conversion process between the constants and expressions and attribute types follows the rules described for JavaBeans component properties in Setting JavaBeans Component Properties.

The Duke’s Bookstore page tut-install/javaeetutorial5/examples/web/bookstore3/web/bookcatalog.jsp calls the catalog tag, which has two attributes. The first attribute, a reference to a book database object, is set by an EL expression. The second attribute, which sets the color of the rows in a table that represents the bookstore catalog, is set with a String constant.

<sc:catalog bookDB ="${bookDB}" color="#cccccc">

Fragment Attributes

A JSP fragment is a portion of JSP code passed to a tag handler that can be invoked as many times as needed. You can think of a fragment as a template that is used by a tag handler to produce customized content. Thus, unlike a simple attribute which is evaluated by the container, a fragment attribute is evaluated by a tag handler during tag invocation.

To declare a fragment attribute, you use the fragment attribute of the attribute directive (see Declaring Tag Attributes in Tag Files) or use the fragment subelement of the attribute TLD element (see Declaring Tag Attributes for Tag Handlers). You define the value of a fragment attribute by using a jsp:attribute element. When used to specify a fragment attribute, the body of the jsp:attribute element can contain only static text and standard and custom tags; it cannot contain scripting elements (see Chapter 9, Scripting in JSP Pages).

JSP fragments can be parameterized by means of expression language (EL) variables in the JSP code that composes the fragment. The EL variables are set by the tag handler, thus allowing the handler to customize the fragment each time it is invoked (see Declaring Tag Variables in Tag Files, and Declaring Tag Variables for Tag Handlers).

The catalog tag discussed earlier accepts two fragments: normalPrice, which is displayed for a product that’s full price, and onSale, which is displayed for a product that’s on sale.

<sc:catalog bookDB ="${bookDB}" color="#cccccc">
    <jsp:attribute name="normalPrice">
        <fmt:formatNumber value="${price}" type="currency"/>
  </jsp:attribute>
    <jsp:attribute name="onSale">
        <strike><fmt:formatNumber value="${price}"
            type="currency"/></strike><br/>
        <font color="red"><fmt:formatNumber value="${salePrice}"
            type="currency"/></font>
    </jsp:attribute>
</sc:catalog>

The tag executes the normalPrice fragment, using the values for the price EL variable, if the product is full price. If the product is on sale, the tag executes the onSale fragment using the price and salePrice variables.

Dynamic Attributes

A dynamic attribute is an attribute that is not specified in the definition of the tag. Dynamic attributes are used primarily by tags whose attributes are treated in a uniform manner but whose names are not necessarily known at development time.

For example, this tag accepts an arbitrary number of attributes whose values are colors and outputs a bulleted list of the attributes colored according to the values:

<colored:colored color1="red" color2="yellow" color3="blue"/>

You can also set the value of dynamic attributes using an EL expression or using the jsp:attribute element.

Deferred Value

A deferred value attribute is one that accepts deferred value expressions, which are described in Value Expressions.

Deferred Method

A deferred method attribute is one that accepts deferred method expressions, which are described in Method Expressions.

Dynamic Attribute or Deferred Expression

This kind of attribute can accept a String literal, a scriptlet expression, or an EL expression, including deferred expressions.

jsp:attribute Element

The jsp:attribute element allows you to define the value of a tag attribute in the body of an XML element instead of in the value of an XML attribute.

For example, the Duke’s Bookstore template page screendefinitions.jsp uses jsp:attribute to use the output of fmt:message to set the value of the value attribute of tt:parameter:

...
<tt:screen id="/bookcatalog">
    <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>
...

jsp:attribute accepts a name attribute and a trim attribute. The name attribute identifies which tag attribute is being specified. The optional trim attribute determines whether or not white space appearing at the beginning and end of the element body should be discarded. By default, the leading and trailing white space is discarded. The white space is trimmed when the JSP page is translated. If a body contains a custom tag that produces leading or trailing white space, that white space is preserved regardless of the value of the trim attribute.

An empty body is equivalent to specifying "" as the value of the attribute.

The body of jsp:attribute is restricted according to the type of attribute being specified:

Tags with Bodies

A simple tag can contain custom and core tags, HTML text, and tag-dependent body content between the start tag and the end tag.

In the following example, the Duke’s Bookstore application page tut-install/javaeetutorial5/examples/web/bookstore3/web/bookshowcart.jsp uses the JSTL c:if tag to print the body if the request contains a parameter named Clear:

<c:if test="${param.Clear}">
    <font color="#ff0000" size="+2"><strong>
     You just cleared your shopping cart!
     </strong><br>&nbsp;<br></font>
</c:if>

jsp:body Element

You can also explicitly specify the body of a simple tag by using the jsp:body element. If one or more attributes are specified with the jsp:attribute element, then jsp:body is the only way to specify the body of the tag. If one or more jsp:attribute elements appear in the body of a tag invocation but you don’t include a jsp:body element, the tag has an empty body.

Tags That Define Variables

A simple tag can define an EL variable that can be used within the calling page. In the following example, the iterator tag sets the value of the EL variable departmentName as it iterates through a collection of department names.

<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>

Communication between Tags

Custom tags communicate with each other through shared objects. There are two types of shared objects: public and private.

In the following example, the c:set tag creates a public EL variable called aVariable, which is then reused by anotherTag.

<c:set var="aVariable" value="aValue" />
<tt:anotherTag attr1="${aVariable}" />

Nested tags can share private objects. In the next example, an object created by outerTag is available to innerTag. The inner tag retrieves its parent tag and then retrieves an object from the parent. Because the object is not named, the potential for naming conflicts is reduced.

<tt:outerTag>
    <tt:innerTag />
</tt:outerTag>

The Duke’s Bookstore page tut-install/javaeetutorial5/examples/web/bookstore3/web/template/template.jsp uses a set of cooperating tags that share public and private objects to define the screens of the application. These tags are described in A Template Tag Library.

Encapsulating Reusable Content Using Tag Files

A tag file is a source file that contains a fragment of JSP code that is reusable as a custom tag. Tag files allow you to create custom tags using JSP syntax. Just as a JSP page gets translated into a servlet class and then compiled, a tag file gets translated into a tag handler and then compiled.

The recommended file extension for a tag file is .tag. As is the case with JSP files, the tag can be composed of a top file that includes other files that contain either a complete tag or a fragment of a tag file. Just as the recommended extension for a fragment of a JSP file is .jspf, the recommended extension for a fragment of a tag file is .tagf.

The following version of the Hello, World application introduced in Chapter 3, Getting Started with Web Applications uses a tag to generate the response. The response tag, which accepts two attributes (a greeting string and a name) is encapsulated in response.tag:

<%@ attribute name="greeting" required="true" %>
<%@ attribute name="name" required="true" %>
<h2><font color="black">${greeting}, ${name}!</font></h2>

The highlighted line in the greeting.jsp page invokes the response tag if the length of the username request parameter is greater than 0:

<%@ taglib tagdir="/WEB-INF/tags" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core"
     prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions"
    prefix="fn" %>
<html>
<head><title>Hello</title></head>
<body bgcolor="white">
<img src="duke.waving.gif">
 <c:set var="greeting" value="Hello" />
 <h2>${greeting}, my name is Duke. What’s yours?</h2>
<form method="get">
<input type="text" name="username" size="25">
<p></p>
<input type="submit" value="Submit">
<input type="reset" value="Reset">
</form>

<c:if test="${fn:length(param.username) > 0}" >
    <h:response greeting="${greeting}"
        name="${param.username}"/></c:if>
</body>
</html>

    To deploy and run the hello3 application with NetBeans IDE, follow these steps:

  1. In NetBeans IDE, select File->Open Project.

  2. In the Open Project dialog, navigate to:


    tut-install/javaeetutorial5/examples/web/
    
  3. Select the hello3 folder.

  4. Select the Open as Main Project check box.

  5. Click Open Project.

  6. In the Projects tab, right-click the hello3 project, and select Undeploy and Deploy.

  7. To run the application, open the bookstore URL http://localhost:8080/hello3.

    To deploy and run the hello3 application with Ant, follow these steps:

  1. In a terminal window, go to tut-install/javaeetutorial5/examples/web/hello3/.

  2. Type ant. This target will spawn any necessary compilations, copy files to the tut-install/javaeetutorial5/examples/web/hello3/build/ directory, and create a WAR file.

  3. Start the Application Server.

  4. To deploy the example, type ant deploy.

  5. To run the example, open your browser to http://localhost:8080/hello3.

To learn how to configure the example, refer to the deployment descriptor (the web.xml file), which includes the following configurations:

Tag File Location

Tag files can be placed in one of two locations: in the /WEB-INF/tags/ directory or subdirectory of a web application or in a JAR file (see Packaged Tag Files) in the /WEB-INF/lib/ directory of a web application. Packaged tag files require a tag library descriptor (see Tag Library Descriptors), an XML document that contains information about a library as a whole and about each tag contained in the library. Tag files that appear in any other location are not considered tag extensions and are ignored by the web container.

Tag File Directives

Directives are used to control aspects of tag file translation to a tag handler, and to specify aspects of the tag, attributes of the tag, and variables exposed by the tag. Table 8–1 lists the directives that you can use in tag files.

Table 8–1 Tag File Directives

Directive 

Description 

taglib

Identical to taglib directive (see Declaring Tag Libraries) for JSP pages.

include

Identical to include directive (see Reusing Content in JSP Pages) for JSP pages. Note that if the included file contains syntax unsuitable for tag files, a translation error will occur.

tag

Similar to the page directive in a JSP page, but applies to tag files instead of JSP pages. As with the page directive, a translation unit can contain more than one instance of the tag directive. All the attributes apply to the complete translation unit. However, there can be only one occurrence of any attribute or value defined by this directive in a given translation unit. With the exception of the import attribute, multiple attribute or value (re)definitions result in a translation error.

Also used for declaring custom tag properties such as display name. See Declaring Tags.

attribute

Declares an attribute of the custom tag defined in the tag file. See Declaring Tag Attributes in Tag Files.

variable

Declares an EL variable exposed by the tag to the calling page. See Declaring Tag Variables in Tag Files.

Declaring Tags

The tag directive is similar to the JSP page’s page directive but applies to tag files. Some of the elements in the tag directive appear in the tag element of a TLD (see Declaring Tag Handlers). Table 8–2 lists the tag directive attributes.

Table 8–2 tag Directive Attributes

Attribute 

Description 

display-name

(optional) A short name that is intended to be displayed by tools. Defaults to the name of the tag file without the extension .tag.

body-content

(optional) Provides information on the content of the body of the tag. Can be either empty, tagdependent, or scriptless. A translation error will result if JSP or any other value is used. Defaults to scriptless. See body-content Attribute.

dynamic-attributes

(optional) Indicates whether this tag supports additional attributes with dynamic names. The value identifies a scoped attribute in which to place a Map containing the names and values of the dynamic attributes passed during invocation of the tag.

A translation error results if the value of the dynamic-attributes of a tag directive is equal to the value of a name-given of a variable directive or the value of a name attribute of an attribute directive.

small-icon

(optional) Relative path, from the tag source file, of an image file containing a small icon that can be used by tools. Defaults to no small icon. 

large-icon

(optional) Relative path, from the tag source file, of an image file containing a large icon that can be used by tools. Defaults to no large icon. 

description

(optional) Defines an arbitrary string that describes this tag. Defaults to no description. 

example

(optional) Defines an arbitrary string that presents an informal description of an example of a use of this action. Defaults to no example. 

language

(optional) Carries the same syntax and semantics of the language attribute of the page directive.

import

(optional) Carries the same syntax and semantics of the import attribute of the page directive.

pageEncoding

(optional) Carries the same syntax and semantics of the page-Encoding attribute in the page directive.

isELIgnored

(optional) Carries the same syntax and semantics of the isEL-Ignored attribute of the page directive.

body-content Attribute

You specify the type of a tag’s body content using the body-content attribute:

bodycontent="empty | scriptless | tagdependent"

You must declare the body content of tags that do not accept a body as empty. For tags that have a body there are two options. Body content containing custom and standard tags and HTML text is specified as scriptless. All other types of body content (for example, SQL statements passed to the query tag) is specified as tagdependent. If no attribute is specified, the default is scriptless.

Declaring Tag Attributes in Tag Files

To declare the attributes of a custom tag defined in a tag file, you use the attribute directive. A TLD has an analogous attribute element (see Declaring Tag Attributes for Tag Handlers). Table 8–3 lists the attribute directive attributes.

Table 8–3 attribute Directive Attributes

Attribute 

Description 

description

(optional) Description of the attribute. Defaults to no description. 

name

The unique name of the attribute being declared. A translation error results if more than one attribute directive appears in the same translation unit with the same name.

A translation error results if the value of a name attribute of an attribute directive is equal to the value of the dynamic-attributes attribute of a tag directive or the value of a name-given attribute of a variable directive.

required

(optional) Whether this attribute is required (true) or optional (false). Defaults to false.

rtexprvalue

(optional) Whether the attribute’s value can be dynamically calculated at runtime by an expression. Defaults to true. When this element is set to true and the attribute definition also includes either a deferred-value or deferred-method element then the attribute accepts both dynamic and deferred expressions.

type

(optional) The runtime type of the attribute’s value. Defaults to java.lang.String.

deferredValue

(optional) Indicates whether the attribute accepts deferred value expressions. Only one of deferredValue or deferredMethod can be true. If deferredValueType is specified, the default for deferredValue is true. Causes a translation error if specified in a tag file with a JSP version less than 2.1.

deferredValueType

(optional) The type resulting from the evaluation of the attribute’s value expression. The default is java.lang.String if no type is specified. If both deferredValueType and deferredValue are specified, deferredValue must be true. If deferredValue is true, the default of deferredValueType is java.lang.Object. Causes a translation error specified in a tag file with a JSP version less than 2.1.

deferredMethod

(optional) Indicates whether the tag attribute accepts deferred method expressions. If deferredMethod and deferredMethodSignature are specified then deferredMethod must be true. The default of deferredMethod is true if deferredMethodSignature is specified, otherwise the default of deferredMethod is false. The presence of a deferred-method element in an attribute definition precludes the inclusion of a deferred-value element. Causes a translation error if specified in a tag file with a JSP version less than 2.1.

deferredMethodSignature

(optional) The signature of the method to be invoked by the expression defined by the accompanying deferredMethod attribute. If deferredMethod is true and this attribute is not specified, the method signature defaults to void methodName(). Causes a translation error if specified in a tag file with a JSP version less than 2.1.

fragment

(optional) Whether this attribute is a fragment to be evaluated by the tag handler (true) or a normal attribute to be evaluated by the container before being passed to the tag handler.

If this attribute is true:

You do not specify the rtexprvalue attribute. The container fixes the rtexprvalue attribute at true.

You do not specify the type attribute. The container fixes the type attribute at javax.servlet.jsp.tagext.JspFragment.

Defaults to false.

Declaring Tag Variables in Tag Files

Tag attributes are used to customize tag behavior much as parameters are used to customize the behavior of object methods. In fact, using tag attributes and EL variables, it is possible to emulate various types of parameters: IN, OUT, and nested.

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

To emulate OUT or nested parameters, use EL variables. The variable is not initialized by the calling page but instead is set by the tag file. Each type of parameter is synchronized with the calling page at various points according to the scope of the variable. See Variable Synchronization for details.

To declare an EL variable exposed by a tag file, you use the variable directive. A TLD has an analogous variable element (see Declaring Tag Variables for Tag Handlers). Table 8–4 lists the variable directive attributes.

Table 8–4 variable Directive Attributes

Attribute 

Description 

description

(optional) An optional description of this variable. Defaults to no description. 

name-given | name-from-attribute

Defines an EL variable to be used in the page invoking this tag. Either name-given or name-from-attribute must be specified. If name-given is specified, the value is the name of the variable. If name-from-attribute is specified, the value is the name of an attribute whose (translation-time) value at the start of the tag invocation will give the name of the variable.

Translation errors arise in the following circumstances: 

1. Specifying neither name-given nor name-from-attribute or both.

2. If two variable directives have the same name-given.

3. If the value of a name-given attribute of a variable directive is equal to the value of a name attribute of an attribute directive or the value of a dynamic-attributes attribute of a tag directive.

alias

Defines a variable, local to the tag file, to hold the value of the EL variable. The container will synchronize this value with the variable whose name is given in name-from-attribute.

Required when name-from-attribute is specified. A translation error results if used without name-from-attribute.

A translation error results if the value of alias is the same as the value of a name attribute of an attribute directive or the name-given attribute of a variable directive.

variable-class

(optional) The name of the class of the variable. The default is java.lang.String.

declare

(optional) Whether or not the variable is declared. True is the default.

scope

(optional) The scope of the variable. Can be either AT_BEGIN, AT_END, or NESTED. Defaults to NESTED.

Variable Synchronization

The web container handles the synchronization of variables between a tag file and a calling page. Table 8–5 summarizes when and how each object is synchronized according to the object’s scope.

Table 8–5 Variable Synchronization Behavior

Tag File Location 

AT_BEGIN

NESTED

AT_END

Beginning 

Not sync. 

Save 

Not sync. 

Before any fragment invocation using jsp:invoke or jsp:doBody (see Evaluating Fragments Passed to Tag Files)

Tag->page 

Tag->page 

Not sync. 

End 

Tag->page 

Restore 

Tag->page 

If name-given is used to specify the variable name, then the name of the variable in the calling page and the name of the variable in the tag file are the same and are equal to the value of name-given.

The name-from-attribute and alias attributes of the variable directive can be used to customize the name of the variable in the calling page while another name is used in the tag file. When using these attributes, you set the name of the variable in the calling page from the value of name-from-attribute at the time the tag was called. The name of the corresponding variable in the tag file is the value of alias.

Synchronization Examples

The following examples illustrate how variable synchronization works between a tag file and its calling page. All the example JSP pages and tag files reference the JSTL core tag library with the prefix c. The JSP pages reference a tag file located in /WEB-INF/tags with the prefix my.

AT_BEGIN Scope

In this example, the AT_BEGIN scope is used to pass the value of the variable named x to the tag’s body and at the end of the tag invocation.

<%-- callingpage.jsp --%>
<c:set var="x" value="1"/>
${x} <%-- (x == 1) --%>
<my:example>
    ${x} <%-- (x == 2) --%>
</my:example>
${x} <%-- (x == 4) --%>
<%-- example.tag --%>
<%@ variable name-given="x" scope="AT_BEGIN" %>
${x} <%-- (x == null) --%>
<c:set var="x" value="2"/>
<jsp:doBody/>
${x} <%-- (x == 2) --%>
<c:set var="x" value="4"/>
NESTED Scope

In this example, the NESTED scope is used to make a variable named x available only to the tag’s body. The tag sets the variable to 2, and this value is passed to the calling page before the body is invoked. Because the scope is NESTED and because the calling page also had a variable named x, its original value, 1, is restored when the tag completes.

<%-- callingpage.jsp --%>
<c:set var="x" value="1"/>
${x} <%-- (x == 1) --%>
<my:example>
    ${x} <%-- (x == 2) --%>
</my:example>
${x} <%-- (x == 1) --%>
<%-- example.tag --%>
<%@ variable name-given="x" scope="NESTED" %>
${x} <%-- (x == null) --%>
<c:set var="x" value="2"/>
<jsp:doBody/>
${x} <%-- (x == 2) --%>
<c:set var="x" value="4"/>
AT_END Scope

In this example, the AT_END scope is used to return a value to the page. The body of the tag is not affected.

<%-- callingpage.jsp --%>
<c:set var="x" value="1"/>
${x} <%-- (x == 1) --%>
<my:example>
    ${x} <%-- (x == 1) --%>
</my:example>
${x} <%-- (x == 4) --%>
<%-- example.tag --%>
<%@ variable name-given="x" scope="AT_END" %>
${x} <%-- (x == null) --%>
<c:set var="x" value="2"/>
<jsp:doBody/>
${x} <%-- (x == 2) --%>
<c:set var="x" value="4"/>
AT_BEGIN and name-from-attribute

In this example the AT_BEGIN scope is used to pass an EL variable to the tag’s body and make to it available to the calling page at the end of the tag invocation. The name of the variable is specified by the value of the attribute var. The variable is referenced by a local name, result, in the tag file.

<%-- callingpage.jsp --%>
<c:set var="x" value="1"/>
${x} <%-- (x == 1) --%>
<my:example var="x">
    ${x} <%-- (x == 2) --%>
    ${result} <%-- (result == null) --%>
    <c:set var="result" value="invisible"/>
</my:example>
${x} <%-- (x == 4) --%>
${result} <%-- (result == ”invisible’) --%>
<%-- example.tag --%>
<%@ attribute name="var" required="true" rtexprvalue="false"%>
<%@ variable alias="result" name-from-attribute="var"
    scope="AT_BEGIN" %>
${x} <%-- (x == null) --%>
${result} <%-- (result == null) --%>
<c:set var="x" value="ignored"/>
<c:set var="result" value="2"/>
<jsp:doBody/>
${x} <%-- (x == ”ignored’) --%>
${result} <%-- (result == 2) --%>
<c:set var="result" value="4"/>

Evaluating Fragments Passed to Tag Files

When a tag file is executed, the web container passes it two types of fragments: fragment attributes and the tag body. Recall from the discussion of fragment attributes that fragments are evaluated by the tag handler as opposed to the web container. Within a tag file, you use the jsp:invoke element to evaluate a fragment attribute and use the jsp:doBody element to evaluate a tag file body.

The result of evaluating either type of fragment is sent to the response or is stored in an EL variable for later manipulation. To store the result of evaluating a fragment to an EL variable, you specify the var or varReader attribute. If var is specified, the container stores the result in an EL variable of type String with the name specified by var. If varReader is specified, the container stores the result in an EL variable of type java.io.Reader, with the name specified by varReader. The Reader object can then be passed to a custom tag for further processing. A translation error occurs if both var and varReader are specified.

An optional scope attribute indicates the scope of the resulting variable. The possible values are page (default), request, session, or application. A translation error occurs if you use this attribute without specifying the var or varReader attribute.

Custom Tag Examples

This section introduces examples that demonstrate using custom tags.

Simple Attribute Example

The Duke’s Bookstore shipDate tag, defined in tut-install/javaeetutorial5/examples/web/bookstore3/web/WEB-INF/tags/shipDate.tag, is a custom tag that has a simple attribute. The tag generates the date of a book order according to the type of shipping requested.

<%@ taglib prefix="sc" tagdir="/WEB-INF/tags" %>
<h3><fmt:message key="ThankYou"/> ${param.cardname}.</h3><br>
<fmt:message key="With"/>
 <em><fmt:message key="${param.shipping}"/></em>,
  <fmt:message key="ShipDateLC"/>
<sc:shipDate shipping="${param.shipping}" />

The tag determines the number of days until shipment from the shipping attribute passed to it by the page tut-install/javaeetutorial5/examples/web/bookstore3/web/bookreceipt.jsp. From the number of days, the tag computes the ship date. It then formats the ship date.

<%@ attribute name="shipping" required="true" %>

<jsp:useBean id="now" class="java.util.Date" />
<jsp:useBean id="shipDate" class="java.util.Date" />
<c:choose>
  <c:when test="${shipping == ’QuickShip’}">
    <c:set var="days" value="2" />
  </c:when>
  <c:when test="${shipping == ’NormalShip’}">
    <c:set var="days" value="5" />
  </c:when>
  <c:when test="${shipping == ’SaverShip’}">
     <c:set var="days" value="7" />
  </c:when>
</c:choose>
<jsp:setProperty name="shipDate" property="time"
    value="${now.time + 86400000 * days}" />
<fmt:formatDate value="${shipDate}" type="date"
    dateStyle="full"/>.<br><br>

Simple and Fragment Attribute and Variable Example

The Duke’s Bookstore catalog tag, defined in tut-install/javaeetutorial5/examples/web/bookstore3/web/WEB-INF/tags/catalog.tag, is a custom tag with simple and fragment attributes and variables. The tag renders the catalog of a book database as an HTML table. The tag file declares that it sets variables named price and salePrice using variable directives. The fragment normalPrice uses the variable price, and the fragment onSale uses the variables price and salePrice. Before the tag invokes the fragment attributes using the jsp:invoke element, the web container passes values for the variables back to the calling page.

<%@ attribute name="bookDB" required="true"
    type="database.BookDB" %>
<%@ attribute name="color" required="true" %>
<%@ attribute name="normalPrice" fragment="true" %>
<%@ attribute name="onSale" fragment="true" %>

<%@ variable name-given="price" %>
 <%@ variable name-given="salePrice" %>

<center>
<table>
<c:forEach var="book" begin="0" items="${bookDB.books}">
    <tr>
    <c:set var="bookId" value="${book.bookId}" />
    <td bgcolor="${color}">
         <c:url var="url" value="/bookdetails" >
            <c:param name="bookId" value="${bookId}" />
        </c:url>
        <a href="${url}"><
            strong>${book.title}&nbsp;</strong></a></td>
     <td bgcolor="${color}" rowspan=2>
    <c:set var="salePrice" value="${book.price * .85}" />
    <c:set var="price" value="${book.price}" />
    <c:choose>
        <c:when test="${book.onSale}" >
            <jsp:invoke fragment="onSale" />
        </c:when>
        <c:otherwise>
            <jsp:invoke fragment="normalPrice"/>
        </c:otherwise>
    </c:choose>

    &nbsp;</td>

 ...
</table>
</center>

The page bookcatalog.jsp invokes the catalog tag that has the simple attributes bookDB, which contains catalog data, and color, which customizes the coloring of the table rows. The formatting of the book price is determined by two fragment attributes, normalPrice and onSale, that are conditionally invoked by the tag according to data retrieved from the book database.

<sc:catalog bookDB ="${bookDB}" color="#cccccc">
    <jsp:attribute name="normalPrice">
        <fmt:formatNumber value="${price}" type="currency"/>
    </jsp:attribute>
    <jsp:attribute name="onSale">
        <strike>
        <fmt:formatNumber value="${price}" type="currency"/>
        </strike><br/>
        <font color="red">
        <fmt:formatNumber value="${salePrice}" type="currency"/>
        </font>
    </jsp:attribute>
</sc:catalog>

The screen produced by tut-install/javaeetutorial5/examples/web/bookstore3/web/bookcatalog.jsp is shown in Figure 8–2. You can compare it to the version in Figure 5–2.

Figure 8–2 Book Catalog

Screen capture of Duke's Bookstore book catalog, with
titles, authors, prices, and "Add to Cart" links. Three discount prices are
highlighted.

Dynamic Attribute Example

The following code implements the tag discussed in Dynamic Attributes. An arbitrary number of attributes whose values are colors are stored in a Map named by the dynamic-attributes attribute of the tag directive. The JSTL forEach tag is used to iterate through the Map and the attribute keys and colored attribute values are printed in a bulleted list.

<%@ tag dynamic-attributes="colorMap"%>
<ul>
<c:forEach var="color" begin="0" items="${colorMap}">
    <li>${color.key} =
         <font color="${color.value}">${color.value}</font></li>
</c:forEach>
</ul>

Tag Library Descriptors

If you want to redistribute your tag files or implement your custom tags with tag handlers written in Java, you must declare the tags in a tag library descriptor (TLD). A tag library descriptor is an XML document that 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.

Tag library descriptor file names must have the extension .tld and must be packaged in the /WEB-INF/ directory or subdirectory of the WAR file or in the /META-INF/ directory or subdirectory of a tag library packaged in a JAR. If a tag is implemented as a tag file and is packaged in /WEB-INF/tags/ or a subdirectory, a TLD will be generated automatically by the web container, though you can provide one if you wish.

Most containers set the JSP version of this automatically generated TLD (called an implicit TLD) to 2.0. Therefore, in order to take advantage of JSP 2.1 features, you must provide a TLD that sets the JSP version to 2.1 if you don’t have a TLD already. This TLD must be named implicit.tld and placed into the same directory as the tag files.

You set the JSP version using the version attribute of the root taglib element that of the TLD, as shown here:

<taglib
    xsi:schemaLocation=
    "http://java.sun.com/xml/ns/javaee web-
        jsptaglibrary_2_1.xsd"
    xmlns="http://java.sun.com/xml/ns/javaee"|
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     version="2.1">

Table 8–6 lists the subelements of the taglib element.

Table 8–6 taglib Subelements

Element 

Description 

description

(optional) A string describing the use of the tag library. 

display-name

(optional) Name intended to be displayed by tools. 

icon

(optional) Icon that can be used by tools. 

tlib-version

The tag library’s version. 

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. 

validator

See validator Element.

listener

See listener Element.

tag-file | tag

Declares the tag files or tags defined in the tag library. See Declaring Tag Files and Declaring Tag Handlers. A tag library is considered invalid if a tag-file element has a name subelement with the same content as a name subelement in a tag element.

function

Zero or more EL functions (see Functions) defined in the tag library.

tag-extension

(optional) Extensions that provide extra information about the tag library for tools. 

Top-Level Tag Library Descriptor Elements

This section describes some top-level TLD elements. Subsequent sections describe how to declare tags defined in tag files, how to declare tags defined in tag handlers, and how to declare tag attributes and variables.

validator Element

This element defines an optional tag library validator that can be used to validate the conformance of any JSP page importing this tag library to its requirements. Table 8–7 lists the subelements of the validator element.

Table 8–7 validator Subelements

Element 

Description 

validator-class

The class implementing javax.servlet.jsp.tagext.TagLibraryValidator

init-param

(optional) Initialization parameters 

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 that of 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.

Declaring Tag Files

Although not required for tag files, providing a TLD allows you to share the tag across more than one tag library and lets you import the tag library using a URI instead of the tagdir attribute.

tag-file TLD Element

A tag file is declared in the TLD using a tag-file element. Its subelements are listed in Table 8–8.

Table 8–8 tag-file Subelements

Element 

Description 

description

(optional) A description of the tag. 

display-name

(optional) Name intended to be displayed by tools. 

icon

(optional) Icon that can be used by tools. 

name

The unique tag name. 

path

Where to find the tag file implementing this tag, relative to the root of the web application or the root of the JAR file for a tag library packaged in a JAR. This must begin with /WEB-INF/tags/ if the tag file resides in the WAR, or /META-INF/tags/ if the tag file resides in a JAR.

example

(optional) Informal description of an example use of the tag. 

tag-extension

(optional) Extensions that provide extra information about the tag for tools. 

Unpackaged Tag Files

Tag files placed in a subdirectory of /WEB-INF/tags/ do not require a TLD file and don’t have to be packaged. Thus, to create reusable JSP code, you simply create a new tag file and place the code inside it.

The web container generates an implicit tag library for each directory under and including /WEB-INF/tags/. There are no special relationships between subdirectories; they are allowed simply for organizational purposes. For example, the following web application contains three tag libraries:

/WEB-INF/tags/
/WEB-INF/tags/a.tag
/WEB-INF/tags/b.tag
/WEB-INF/tags/foo/
/WEB-INF/tags/foo/c.tag
/WEB-INF/tags/bar/baz/
/WEB-INF/tags/bar/baz/d.tag

The implicit TLD for each library has the following values:

So, for the example, the implicit TLD for the /WEB-INF/tags/bar/baz/ directory would be as follows:

<taglib>
    <tlib-version>1.0</tlib-version>
    <short-name>bar-baz</short-name>
    <tag-file>
        <name>d</name>
        <path>/WEB-INF/tags/bar/baz/d.tag</path>
    </tag-file>
</taglib>

Despite the existence of an implicit tag library, a TLD in the web application can still create additional tags from the same tag files. To accomplish this, you add a tag-file element with a path that points to the tag file.

Packaged Tag Files

Tag files can be packaged in the /META-INF/tags/ directory in a JAR file installed in the /WEB-INF/lib/ directory of the web application. Tags placed here are typically part of a reusable library of tags that can be used easily in any web application.

Tag files bundled in a JAR require a tag library descriptor. Tag files that appear in a JAR but are not defined in a TLD are ignored by the web container.

When used in a JAR file, the path subelement of the tag-file element specifies the full path of the tag file from the root of the JAR. Therefore, it must always begin with /META-INF/tags/.

Tag files can also be compiled into Java classes and bundled as a tag library. This is useful when you wish to distribute a binary version of the tag library without the original source. If you choose this form of packaging, you must use a tool that produces portable JSP code that uses only standard APIs.

Declaring Tag Handlers

When tags are implemented with tag handlers written in Java, each tag in the library must be declared in the TLD with a tag element. The tag element contains the tag name, the class of its tag handler, information on the tag’s attributes, and information on the variables created by the tag (see Tags That Define Variables).

Each attribute declaration contains an indication of whether the attribute is required, whether its value can be determined by request-time expressions, the type of the attribute, and whether the attribute is a fragment. Variable information can be given directly in the TLD or through a tag extra info class. Table 8–9 lists the subelements of the tag element.

Table 8–9 tag Subelements

Element 

Description 

description

(optional) A description of the tag. 

display-name

(optional) name intended to be displayed by tools. 

icon

(optional) Icon that can be used by tools. 

name

The unique tag name. 

tag-class

The fully qualified name of the tag handler class. 

tei-class

(optional) Subclass of javax.servlet.jsp.tagext.TagExtraInfo. See Declaring Tag Variables for Tag Handlers.

body-content

The body content type. See body-content Element.

variable

(optional) Declares an EL variable exposed by the tag to the calling page. See Declaring Tag Variables for Tag Handlers.

attribute

Declares an attribute of the custom tag. See Declaring Tag Attributes for Tag Handlers.

dynamic-attributes

Whether the tag supports additional attributes with dynamic names. Defaults to false. If true, the tag handler class must implement the javax.servlet.jsp.tagext.DynamicAttributes interface.

example

(optional) Informal description of an example use of the tag. 

tag-extension

(optional) Extensions that provide extra information about the tag for tools. 

body-content Element

You specify the type of body that is valid for a tag by using the body-content element. This element is used by the web container to validate that a tag invocation has the correct body syntax and is used by page-composition tools to assist the page author in providing a valid tag body. There are three possible values:

Declaring Tag Attributes for Tag Handlers

For each tag attribute, you must specify whether the attribute is required, whether the value can be determined by an expression, the type of the attribute in an attribute element (optional), and whether the attribute is a fragment. If the rtexprvalue element is true or yes, then the type element defines the return type expected from any expression specified as the value of the attribute. For static values, the type is always java.lang.String. An attribute is specified in a TLD in an attribute element. Table 8–10 lists the subelements of the attribute element.

Table 8–10 attribute Subelements

Element 

Description 

description

(optional) A description of the attribute. 

name

The unique name of the attribute being declared. A translation error results if more than one attribute element appears in the same tag with the same name.

required

(optional) Whether the attribute is required. The default is false.

rtexprvalue

(optional) Whether the attribute’s value can be dynamically calculated at runtime by an EL expression. The default is false. When this element is set to true and the attribute definition also includes either a deferred-value or deferred-method element then the attribute accepts both dynamic and deferred expressions.

type

(optional) The runtime type of the attribute’s value. Defaults to java.lang.String if not specified.

fragment

(optional) Whether this attribute is a fragment to be evaluated by the tag handler (true) or a normal attribute to be evaluated by the container before being passed to the tag handler.

If this attribute is true:

You do not specify the rtexprvalue attribute. The container fixes the rtexprvalue attribute at true.

You do not specify the type attribute. The container fixes the type attribute at javax.servlet.jsp.tagext.JspFragment.

Defaults to false.

deferred-value

(optional) Indicates that the tag attribute accepts deferred value expressions. This element includes an optional type child element, which indicates the type of object to which the expression resolves. If no type element is included, the type is java.lang.Object. Either the deferred-value or deferred-method element (but not both) can be defined for the same attribute.

deferred-method

(optional) Indicates that the tag attribute accepts deferred method expressions. This element includes an optional method-signature child element, which indicates the signature of the method that the expression invokes. If no method signature is defined, the method signature default is void methodName(). Either the deferred-value or deferred-method element (but not both) can be defined for the same attribute.

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

The tag element for a tag that outputs its body if a test evaluates to true declares that the test attribute is required and that its value can be set by a runtime expression.

<tag>
    <name>present</name>
         <tag-class>condpkg.IfSimpleTag</tag-class>
    <body-content>scriptless</body-content>
    ...
    <attribute>
        <name>test</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    ...
</tag>

Declaring Tag Variables for Tag Handlers

The example described in Tags That Define Variables defines an EL variable departmentName:

    <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>

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

There are two ways to provide this information: by specifying the variable TLD subelement or by defining a tag extra info class and including the tei-class element in the TLD (see TagExtraInfo Class). Using the variable element is simpler but less dynamic. With the variable element, the only aspect of the variable that you can specify at runtime is its name (with the name-from-attribute element). If you provide this information in a tag extra info class, you can also specify the type of the variable at runtime.

Table 8–11 lists the subelements of the variable element.

Table 8–11 variable Subelements

Element 

Description 

description

(optional) A description of the variable. 

name-given | name-from-attribute

Defines an EL variable to be used in the page invoking this tag. Either name-given or name-from-attribute must be specified. If name-given is specified, the value is the name of the variable. If name-from-attribute is specified, the value is the name of an attribute whose (translation-time) value at the start of the tag invocation will give the name of the variable.

Translation errors arise in the following circumstances: 

  • Specifying neither name-given nor name-from-attribute or both.

  • If two variable elements have the same name-given.

variable-class

(optional) The fully qualified name of the class of the object. java.lang.String is the default.

declare

(optional) Whether or not the object is declared. True is the default. A translation error results if both declare and fragment are specified.

scope

(optional) The scope of the variable defined. Can be either AT_BEGIN, AT_END, or NESTED (see Table 8–12). Defaults to NESTED.

Table 8–12 summarizes a variable’s availability according to its declared scope.

Table 8–12 Variable Availability

Value 

Availability 

NESTED

Between the start tag and the end tag. 

AT_BEGIN

From the start tag until the scope of any enclosing tag. If there’s no enclosing tag, then to the end of the page. 

AT_END

After the end tag until the scope of any enclosing tag. If there’s no enclosing tag, then to the end of the page. 

You can define the following variable element for the tlt:iterator tag:

<tag>
    <variable>
        <name-given>var</name-given>
        <variable-class>java.lang.String</variable-class>
        <declare>true</declare>
        <scope>NESTED</scope>
    </variable>
</tag>

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 method, doTag, which is 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.

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).

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). 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 setXXX, where XXX 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.attributeName = attributeName;
}

Deferred value attributes and deferred method attributes are primarily used by JavaServer Faces technology. See Getting the Attribute Values 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 5, JavaServer Pages Technology 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, iterator.next());
        getJspBody().invoke(null);
    }
}
public void setVar(String var) {
    this.var = var;
}
public void setGroup(Collection group) {
    this.group = group;
    if(group.size() > 0)
        iterator = group.iterator();
}

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

Table 8–13 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 in the same web application 

The life of the application 

TagExtraInfo Class

Declaring Tag Variables for Tag Handlers discussed how to provide information about tag variables in the tag library descriptor. This section describes 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:

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 cid="con01" ... >
     ...
 </tt:connection>
<tt:query id="balances" connectionId="con01">
     SELECT account, balance FROM acct_table
         where customer_number = ?
    <tt:param value="${requestScope.custNumber}" />
</tt:query>

<tt:connection ... >
    <tt:query cid="balances">
         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>

Tag Handler Examples

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 5, JavaServer Pages Technology 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.

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 id="myorg" class="myorg.Organization"/>
    <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 by means of 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, iterator.next());
        getJspBody().invoke(null);
    }
}
public void setVar(String var) {
    this.var = var;
}
public void setGroup(Collection group) {
    this.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 tags (definition, screen, and parameter) to 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, tut-install/javaeetutorial5/examples/web/bookstore3/web/template/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 tut-install/javaeetutorial5/examples/web/bookstore3/web/template/screendefinitions.jspf 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 id="/bookstore">
    <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 id="/bookcatalog">
    <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 handlers (DefinitionTag, ScreenTag, ParameterTag, and InsertTag) that 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, tut-install/javaeetutorial5/examples/web/bookstore3/src/java/com/sun/bookstore3/template/DefinitionTag.java 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 tut-install/javaeetutorial5/examples/web/bookstore3/src/java/com/sun/bookstore3/template/Definition.java, 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)ir.next());
        // 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 8–14 shows the contents of the screen definitions hash table for the Duke’s Bookstore application.

Table 8–14 Screen Definitions

Screen ID 

Title 

Banner 

Body 

/bookstore

Duke’s Bookstore

/banner.jsp

/bookstore.jsp

/bookcatalog

Book Catalog

/banner.jsp

/bookcatalog.jsp

/bookdetails

Book Description

/banner.jsp

/bookdetails.jsp

/bookshowcart

Shopping Cart

/banner.jsp

/bookshowcart.jsp

/bookcashier

Cashier

/banner.jsp

/bookcashier.jsp

/bookreceipt

Receipt

/banner.jsp

/bookreceipt.jsp

If the URL passed in the request is /bookstore, the Definition object contains the items from the first row of Table 8–14 (see Table 8–15).

Table 8–15 Definition Object Contents for URL /bookstore

Title 

Banner 

Body 

Duke’s Bookstore

/banner.jsp

/bookstore.jsp

The parameters for the URL /bookstore are shown in Table 8–16. 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 8–16 Parameters for the URL /bookstore

Parameter Name 

Parameter Value 

isDirect

title

Duke’s Bookstore

true

banner

/banner.jsp

false

body

/bookstore.jsp

false

tut-install/javaeetutorial5/examples/web/bookstore3/src/java/com/sun/bookstore3/template/InsertTag.java 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());
    }
}