July 7, 2011, 4:25 p.m.
posted by reo
Tag ElementEach 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 (page 437)). Each attribute declaration contains an indication of whether the attribute is required or not, whether its value can be determined by request-time expressions, and the type of the attribute (see Attribute Element (page 444)). A tag is specified in a TLD in a tag element. The subelements of tag are listed in Figure.
The following sections will describe the methods and TLD elements that you need to develop for each type of tag introduced in Types of Tags (page 436). 3 Simple TagsTag HandlersThe handler for a simple tag must implement the doStartTag and doEndTagmethods of the Tag interface. The doStartTag method is invoked when the start tag is encountered. This method returns SKIP_BODY because a simple tag has no body. The doEndTag method is invoked when the end tag is encountered. The doEndTag method needs to return EVAL_PAGE if the rest of the page needs to be evaluated; otherwise, it should return SKIP_PAGE. The simple tag discussed in the first section: <tt:simple /> would be implemented by the following tag handler:
public SimpleTag extends TagSupport {
public int doStartTag() throws JspException {
try {
pageContext.getOut().print("Hello.");
} catch (Exception ex) {
throw new JspTagException("SimpleTag: " +
ex.getMessage());
}
return SKIP_BODY;
}
public int doEndTag() {
return EVAL_PAGE;
}
}
Body-Content ElementTags without bodies must declare that their body content is empty using the bodycontent element: <body-content>empty</body-content> 4 Tags with AttributesDefining Attributes in a Tag HandlerFor 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 the TagSupport class, you do not need to define the property and set and get methods as these are already defined by TagSupport. 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 (page 410)). Attribute ElementFor 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 always java.lang.String. If the rtexprvalue element is true or yes, then the typeelement 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 the logic:present tag declares that parameterattribute 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 ValidationThe documentation for a tag library should describe valid values for tag attributes. When a JSP page is translated, a Web container will enforce any constraints contained in the TLD element for each attribute. The attributes passed to a tag can also be validated at translation time with the isValid method of a class derived from TagExtraInfo. This class is also used to provide information about scripting variables defined by the tag (see Providing Information About the Scripting Variable (page 449)). The isValid method is passed the attribute information in a TagData object, which contains attribute-value tuples for each of the tag's attributes. Since the validation occurs at translation time, the value of an attribute that is computed at request time will be set to TagData.REQUEST_TIME_VALUE. The tag <tt:twaattr1="value1"/> has the following TLD attributeelement:
<attribute>
<name>attr1</name>
<required>true</required>
<rtexprvalue>true</a>
</attribute
This declaration indicates that the value of attr1 can be determined at runtime. The following isValidmethod checks that the value of attr1 is a valid boolean value. Note that since the value of attr1 can be computed at runtime, isValid must check whether the tag user has chosen to provide a runtime value.
public class TwaTEI extends TagExtraInfo {
public boolean isValid(Tagdata data) {
Object o = data.getAttribute("attr1");
if (o != null && o != TagData.REQUEST_TIME_VALUE) {
if (o.toLowerCase().equals("true") ||
o.toLowerCase().equals("false") )
return true;
else
return false;
}
else
return true;
}
}
5 Tags with BodiesTag HandlersA tag handler for a tag with a body is implemented differently depending on whether the tag handler needs to interact with the body or not. By interact, we mean that the tag handler reads or modifies the contents of the body. Tag Handler Does Not Interact with the Body. If the tag handler does not need to interact with the body, the tag handler should implement the Tag interface (or be derived from TagSupport). If the body of the tag needs to be evaluated, the doStartTag method needs to return EVAL_BODY_INCLUDE; otherwise, it should return SKIP_BODY. If a tag handler needs to iteratively evaluate the body it should implement the IterationTag interface or be derived from TagSupport. It should return EVAL_BODY_AGAIN from the doStartTag and doAfterBody methods if it determines that the body needs to be evaluated again. Tag Handler Interacts with the Body. If the tag handler needs to interact with the body, the tag handler must implement BodyTag (or be derived from BodyTagSupport). Such handlers typically implement the doInitBody and the doAfterBodymethods. These methods interact with body content passed to the tag handler by the JSP page's servlet. A body content supports several methods to read and write its contents. A tag handler can use the body content's getString or getReader methods to extract information from the body and the writeOut(out) method to write the body contents to an out stream. The writer supplied to the writeOut method is obtained using the tag handler's getPreviousOut method. This method is used to ensure that a tag handler's results are available to an enclosing tag handler. If the body of the tag needs to be evaluated, the doStartTag method needs to return EVAL_BODY_BUFFERED; otherwise, it should return SKIP_BODY. doInitBodyMethodThe 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. doAfterBodyMethodThe doAfterBody method is called after the body content is evaluated. Like the doStartTag method, doAfterBody must return an indication of whether to continue evaluating the body. Thus, if the body should be evaluated again, as would be the case if you were implementing an iteration tag, doAfterBody should return EVAL_BODY_BUFFERED; otherwise doAfterBodyshould return SKIP_BODY. releaseMethodA 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 an SQL query) and passes it to a object that executes the query. Since the body does not need to be reevaluated, doAfterBody returns SKIP_BODY.
public class QueryTag extends BodyTagSupport {
public int doAfterBody() throws JspTagException {
BodyContent bc = getBodyContent();
// get the bc as string
String query = bc.getString();
// clean up
bc.clearBody();
try {
Statement stmt = connection.createStatement();
result = stmt.executeQuery(query);
} catch (SQLException e) {
throw new JspTagException("QueryTag: " +
e.getMessage());
}
return SKIP_BODY;
}
}
Body-Content ElementFor 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 l ogic:present tag. All other types of body content, for example, SQL statements passed to the query tag, would be labeled tagdependent. 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. 6 Tags That Define Scripting VariablesTag HandlersA tag handler is responsible for creating and setting the object referred to by the scripting variable into a context accessible from the page. It does this by using the pageContext.setAttribute(name,value,scope) or pageContext.setAttribute(name,value) methods. Typically, an attribute passed to the custom tag specifies the name of the scripting variable object; this name can be retrieved by invoking the attribute's get method described in Using Scope Objects (page 373). 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 Figure. The scope constrains the accessibility and lifetime of the object.
Providing Information About the Scripting VariableThe example described in Tags That Define Scripting Variables (page 437) 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. In order to do the code generation, the Web container requires certain information about the scripting variable:
There are two ways to provide this information: by specifying the variableTLD subelement or by defining a tag extra info class and including the teiclass element in the TLD. Using the variable element is simpler, but slightly less flexible. Variable Element. The variable element has the following subelements:
One of name-given or name-from-attribute is required. The following subelements are optional:
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 the variable element. You could define the following variable element for the bean: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. A TagExtraInfo must implement the getVariableInfo method to return an array of VariableInfo objects containing the following information:
The Web container passes a parameter called data to the getVariableInfomethod that contains attribute-value tuples for each of the tag's attributes. These attributes can be used to provide the VariableInfo object with a scripting variable's name and class. The 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 the data.getAttributeStringmethod and used to fill in the VariableInfo constructor. To allow the scripting variable book to be used in the rest of the page, the scope of book is set to be AT_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 the tagelement. Thus, the tei-class element for DefineTei would be: <tei-class>org.apache.struts.taglib.bean.DefineTagTei </tei-class> 7 Cooperating TagsTags cooperate by sharing objects. JSP technology supports two styles of object sharing. The first style requires that a shared object be named and stored in the page context (one of the implicit objects accessible to both JSP pages and tag handlers). To access objects created and named by another tag, a tag handler uses the pageContext.getAttribute(name, scope) method. In the second style of object sharing, an object created by the enclosing tag handler of a group of nested tags is available to all inner tag handlers. This form of object sharing has the advantage that it uses a private namespace for the objects, thus reducing the potential for naming conflicts. To access an object created by an enclosing tag, a tag handler must first obtain its enclosing tag with the static method TagSupport.findAncestorWithClass(from, class) or the TagSupport.getParent method. The former method should be used when a specific nesting of tag handlers cannot be guaranteed. Once the ancestor has been retrieved, a tag handler can access any statically or dynamically created objects. Statically created objects are members of the parent. Private objects can also be created dynamically created. Such objects can be stored in a tag handler with the setValue method and retrieved with the getValue method. The following example illustrates a tag handler that supports both the named and private object approaches to sharing objects. In the example, the handler for a query tag checks whether an attribute named connection has been set in the doStartTag method. If the connection attribute has been set, the handler retrieves the connection object from the page context. Otherwise, the tag handler first retrieves the tag handler for the enclosing tag, and then retrieves the connection object from that handler.
public class QueryTag extends BodyTagSupport {
private String connectionId;
public int doStartTag() throws JspException {
String cid = getConnection();
if (cid != null) {
// there is a connection id, use it
connection =(Connection)pageContext.
getAttribute(cid);
} else {
ConnectionTag ancestorTag =
(ConnectionTag)findAncestorWithClass(this,
ConnectionTag.class);
if (ancestorTag == null) {
throw new JspTagException("A query without
a connection attribute must be nested
within a connection tag.");
}
connection = ancestorTag.getConnection();
}
}
}
The query tag implemented by this tag handler could be used in either of the following ways:
<tt:connection id="con01" ....> ... </tt:connection>
<tt:query id="balances" connection="con01">
SELECT account, balance FROM acct_table
where customer_number = <%= request.getCustno()%>
</tt:query>
<tt:connection ...>
<x:query id="balances">
SELECT account, balance FROM acct_table
where customer_number = <%= request.getCustno()%>
</x:query>
</tt:connection>
The TLD for the tag handler must indicate that the connection attribute is optional with the following declaration:
<tag>
...
<attribute>
<name>connection</name>
<required>false</required>
</attribute>
</tag>
|
- Comment