Chapter 20. Developing Custom Tag Libraries 363 public class TagSupport implements IterationTag, Serializable { protected PageContext pageContext; public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; } The JSP container calls this method before the tag handler is used. The TagSupport implementation simply sets an instance variable to the current PageContext object. The PageContext provides access to the request and response object and all the JSP scope variables, and it implements a number of utility methods the tag handler may use. We will use most of these methods in the examples in this chapter. Appendix D includes a complete list of all PageContext methods. When the start tag is encountered, the JSP container calls the doStartTag( ) method, implemented like this in the TagSupport class: public int doStartTag( ) throws JspException { return SKIP_BODY; } This method gives the tag handler a chance to initialize itself, perhaps verifying that all attributes have valid values. Another use for this method is to decide what to do with the element's body content, if a body exists. The method returns an int, which must be one of two values defined by the Tag interface: SKIP_BODY or EVAL_BODY_INCLUDE. The default implementation returns SKIP_BODY. As the name implies, this tells the JSP container to ignore the body completely. If EVAL_BODY_INCLUDE is returned, the JSP container processes the body (for instance, executes scripting elements and other actions in the body) and includes the result in the response. You can create a simple conditional tag similar to the JSTL <c:if> action by testing some condition (set by action attributes) in the doStartTag( ) and return either SKIP_BODY or EVAL_BODY_INCLUDE, depending on if the condition is true or false. No matter which value the doStartTag( ) method returns, the JSP container calls doEndTag( ) when it encounters the end tag for the corresponding action element: public int doEndTag( ) throws JspException { return EVAL_PAGE; } This is the method that most tag handlers override to do the real work. It can also return one of two int values defined by the Tag interface. The TagSupport class returns EVAL_PAGE, to tell the JSP container to continue processing the rest of the page. A tag handler can also return SKIP_PAGE, which aborts the processing of the rest of the page. This is appropriate for an action that forwards the processing to another page or sends a redirect response to the browser; the <c:redirect> JSTL action introduced in Chapter 10 is an example. A custom action that can be implemented as a simple tag handler is the <ora:addCookie> action, introduced in Chapter 12. The tag handler class is called Chapter 20. Developing Custom Tag Libraries 364 com.ora.jsp.tags.AddCookieTag and extends the TagSupport class to inherit most of the Tag interface method implementations: package com.ora.jsp.tags; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import com.ora.jsp.util.*; public class AddCookieTag extends TagSupport { The <ora:addCookie> action has two mandatory attributes, name and value, and one optional attribute, maxAge. Each attribute is represented by an instance variable and a standard property setter method: private String name; private String value; private String maxAgeString; public void setName(String name) { this.name = name; } public void setValue(String value) { this.value = value; } public void setMaxAge(String maxAgeString) { this.maxAgeString = maxAgeString; } The purpose of the custom action is to create a new javax.servlet.http.Cookie object with the name, value, and maximum age values specified by the attributes, and to add the cookie to the response. The tag handler class overrides the doEndTag( ) method to carry out this work: public int doEndTag( ) throws JspException { int maxAge = -1; if (maxAgeString != null) { try { maxAge = Integer.valueOf(maxAgeString).intValue( ); } catch (NumberFormatException e) { throw new JspException("Invalid maxAge: " + e.getMessage( )); } } CookieUtils.sendCookie(name, value, maxAge, (HttpServletResponse) pageContext.getResponse( )); return EVAL_PAGE; } The maxAge attribute is optional, so before the corresponding String value is converted into an int, a test is performed to see if it's set or not. You may wonder why similar tests aren't done for the name and value variables. The reason is that the JSP container verifies that all mandatory attributes are set in the custom action element. If a mandatory attribute isn't set, the JSP container refuses to process the page, so you can always be sure that a variable corresponding to a mandatory attribute has a value. At the end of the chapter, I describe how to declare an attributue as mandatory. . interface method implementations: package com.ora.jsp.tags; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import com.ora.jsp.util.*; public. maxAgeString) { this.maxAgeString = maxAgeString; } The purpose of the custom action is to create a new javax.servlet.http.Cookie object with the name, value, and maximum age values specified by the