Home
TOC Index |
Defining Tags
This section describes the properties of tag handlers and TLDs and explains how to develop tag handlers and library descriptor elements for each type of tag introduced in the previous section.
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
orBodyTag
interface. Interfaces can be used to take an existing Java object and make it a tag handler. For newly created handlers, you can use theTagSupport
andBodyTagSupport
classes as base classes. These classes and interfaces are contained in thejavax.servlet.jsp.tagext
package.Tag handler methods defined by the
Tag
andBodyTag
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'sdoStartTag
method. When the end tag of a custom tag is encountered, the handler'sdoEndTag
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 15-1, that are invoked at various stages of processing the tag.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.
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.
TLD file names must have the extension
.tld
. TLD files are stored in theWEB-INF
directory of the WAR file or in a subdirectory ofWEB-INF
.A TLD must begin with an XML document prolog that specifies the version of XML and the document type definition (DTD):
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">Tomcat supports version 1.1 and 1.2 DTDs. However, this chapter documents the 1.2 version because you should use the newer version in any tag libraries that you develop. The template library TLD,
tutorial-template.tld
, conforms to the 1.2 version. The Struts library TLDs conform to the 1.1 version of the DTD, which has fewer elements and uses slightly different names for some of the elements.The root of a TLD is the
taglib
element. The subelements oftaglib
are listed in Table 15-2:
Table 15-2 taglib Subelements Element Description tlib-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
See listener Element
tag
See tag Element
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 thelistener
element is thelistener-class
element, which must contain the fully qualified name of the listener class.tag Element
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 Attribute Element).
A tag is specified in a TLD in a
tag
element. The subelements oftag
are listed in Table 15-3:
Table 15-3 tag Subelements Element Description 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 Providing Information about the Scripting Variable.
body-content
The body content type. See body-content Element and body-content Element.
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
variable
Optional scripting variable information. See Providing Information about the Scripting Variable.
attribute
Tag attribute information. See Attribute Element.
The following sections describe the methods and TLD elements that you need to develop for each type of tag introduced in Types of Tags.
Simple Tags
Tag Handlers
The handler for a simple tag must implement the
doStartTag
anddoEndTag
methods of theTag
interface. ThedoStartTag
method is invoked when the start tag is encountered. This method returnsSKIP_BODY
because a simple tag has no body. ThedoEndTag
method is invoked when the end tag is encountered. ThedoEndTag
method needs to returnEVAL_PAGE
if the rest of the page needs to be evaluated; otherwise, it should returnSKIP_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; } }body-content Element
Tags without bodies must declare that their body content is empty using the
body-content
element:<body-content>empty</body-content>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; }Note that if your attribute is named
id
and your tag handler inherits from theTagSupport
class, you do not need to define the property andset
andget
methods because these are already defined byTagSupport
.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).Attribute Element
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 in an
attribute
element. For static values the type is alwaysjava.lang.String
. If thertexprvalue
element istrue
oryes
, then thetype
element defines the return type expected from any expression specified as the value of the attribute.<attribute> <name>attr1
</name> <required>true|false|yes|no</required> <rtexprvalue>true|false|yes|no</rtexprvalue> <type>fully_qualified_type
</type> </attribute>If a tag attribute is not required, a tag handler should provide a default value.
The
tag
element for thelogic:present
tag declares that theparameter
attribute is not required (because the tag can also test for the presence of other entities such as bean properties) and that its value can be set by a runtime expression.<tag> <name>present</name> <tag-class>org.apache.struts.taglib. logic.PresentTag</tag-class> <body-content>JSP</body-content> ... <attribute> <name>parameter</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> ... </tag>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 fromTagExtraInfo
. 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 aTagData
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 toTagData.REQUEST_TIME_VALUE
.The tag
<tt:twa
attr1="value1"/>
has the following TLDattribute
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
isValid
method checks that the value ofattr1
is a valid Boolean value. Note that since the value ofattr1
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 fromTagSupport
). If the body of the tag needs to be evaluated, thedoStartTag
method needs to returnEVAL_BODY_INCLUDE
; otherwise it should returnSKIP_BODY
.If a tag handler needs to iteratively evaluate the body, it should implement the
IterationTag
interface or be derived fromTagSupport
. It should returnEVAL_BODY_AGAIN
from thedoStartTag
anddoAfterBody
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 fromBodyTagSupport
). Such handlers typically implement thedoInitBody
and thedoAfterBody
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
orgetReader
methods to extract information from the body, and thewriteOut(out)
method to write the body contents to an out stream. The writer supplied to thewriteOut
method is obtained using the tag handler'sgetPreviousOut
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 returnEVAL_BODY_BUFFERED
; otherwise, it should returnSKIP_BODY
.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.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 returnEVAL_BODY_BUFFERED
; otherwise,doAfterBody
should returnSKIP_BODY
.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
returnsSKIP_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; } }body-content Element
For tags that have a body, you must specify the type of the body content using the
body-content
element:<body-content>JSP|tagdependent</body-content>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 labeledtagdependent
.Note that the value of the
body-content
element does not affect the interpretation of the body by the tag handler; the element is only intended to be used by an authoring tool for rendering the body content.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)
orpageContext.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'sget
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 15-4. The scope constrains the accessibility and lifetime of the object.
Providing Information about the Scripting Variable
The example described in Tags That Define Scripting Variables defines a scripting variable
book
that is used for accessing book information:<bean:define id="book" name="bookDB" property="bookDetails" type="database.BookDetails"/> <font color="red" size="+2"> <%=messages.getString("CartRemoved")%> <strong><jsp:getProperty name="book" property="title"/></strong> <br> <br> </font>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:
- Variable name
- Variable class
- Whether the variable refers to a new or existing object
- The availability of 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 thetei-class
element in the TLD. Using thevariable
element is simpler, but slightly less flexible.variable Element
The
variable
element has the following subelements:
name-given
--The variable name as a constantname-from-attribute
--The name of an attribute whose translation-time value will give the name of the variableOne of
name-given
orname-from-attribute
is required. The following subelements are optional:
variable-class
--The fully qualified name of the class of the variable.java.lang.String
is the default.declare
--Whether the variable refers to a new object.True
is the default.scope
--The scope of the scripting variable defined.NESTED
is the default. Table 15-5 describes the availability of the scripting variable and the methods where the value of the variable must be set or reset.The implementation of the Struts
bean:define
tag conforms to the JSP specification version 1.1, which requires you to define a tag extra info class. The JSP specification version 1.2 adds thevariable
element. You could define the followingvariable
element for thebean:define
tag:<tag> <variable> <name-from-attribute>id</name-from-attribute> <variable-class>database.BookDetails</variable-class> <declare>true</declare> <scope>AT_BEGIN</scope> </variable> </tag>TagExtraInfo Class
You define a tag extra info class by extending the class
javax.servlet.jsp.TagExtraInfo
. ATagExtraInfo
must implement thegetVariableInfo
method to return an array ofVariableInfo
objects containing the following information:
- Variable name
- Variable class
- Whether the variable refers to a new object
- The availability of the variable
The Web container passes a parameter called
data
to thegetVariableInfo
method that contains attribute-value tuples for each of the tag's attributes. These attributes can be used to provide theVariableInfo
object with a scripting variable's name and class.The Struts tag library provides information about the scripting variable created by the
bean:define
tag in the DefineTei tag extra info class. Since the name (book
) and class (database.BookDetails
) of the scripting variable are passed in as tag attributes, they can be retrieved with thedata.getAttributeString
method and used to fill in theVariableInfo
constructor. To allow the scripting variablebook
to be used in the rest of the page, the scope ofbook
is set to beAT_BEGIN
.public class DefineTei 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("id"), type, true, VariableInfo.AT_BEGIN) }; } }The fully qualified name of the tag extra info class defined for a scripting variable must be declared in the TLD in the
tei-class
subelement of thetag
element. Thus, thetei-class
element forDefineTei
would be as follows:<tei-class> org.apache.struts.taglib.bean.DefineTagTei </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 theTagSupport.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 thesetValue
method and retrieved with thegetValue
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 thedoStartTag
method. If theconnection
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>
Home
TOC Index |
This tutorial contains information on the 1.0 version of the Java Web Services Developer Pack.
All of the material in The Java Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.