Home
TOC Index |
Examples
The custom 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.
The Struts
logic:iterate
tag retrieves objects from a collection stored in a JavaBeans component and assigns them to a scripting variable. The body of the tag retrieves information from the scripting variable. While elements remain in the collection, theiterate
tag causes the body to be reevaluated.JSP Page
Two Duke's Bookstore application pages,
catalog.jsp
andshowcart.jsp
, use thelogic:iterate
tag to iterate over collections of objects. An excerpt fromcatalog.jsp
is shown below. The JSP page initializes theiterate
tag with a collection (named by theproperty
attribute) of thebookDB
bean. Theiterate
tag sets thebook
scripting variable on each iteration over the collection. ThebookId
property of thebook
variable is exposed as another scripting variable. Properties of both variables are used to dynamically generate a table containing links to other pages and book catalog information.<logic:iterate name="bookDB" property="books" id="book" type="database.BookDetails"> <bean:define id="bookId" name="book" property="bookId" type="java.lang.String"/> <tr> <td bgcolor="#ffffaa"> <a href="<%=request.getContextPath()%> /bookdetails?bookId=<%=bookId%>"> <strong><jsp:getProperty name="book" property="title"/> </strong></a></td> <td bgcolor="#ffffaa" rowspan=2> <jsp:setProperty name="currency" property="amount" value="<%=book.getPrice()%>"/> <jsp:getProperty name="currency" property="format"/> </td> <td bgcolor="#ffffaa" rowspan=2> <a href="<%=request.getContextPath()%> /catalog?Add=<%=bookId%>"> <%=messages.getString("CartAdd")%> </a></td></tr> <tr> <td bgcolor="#ffffff"> <%=messages.getString("By")%> <em> <jsp:getProperty name="book" property="firstName"/> <jsp:getProperty name="book" property="surname"/></em></td></tr> </logic:iterate>Tag Handler
The implementation of the Struts
logic:iterate
tag conforms to the capabilities of the JSP version 1.1 specification, which requires you to extend theBodyTagSupport
class. The JSP version 1.2 specification adds features (described in Tag Handler Does Not Interact with the Body) that simplify programming tags that iteratively evaluate their body. The following discussion is based on an implementation that uses these features.The
logic:iterate
tag supports initializing the collection in several ways: from a collection provided as a tag attribute or from a collection that is a bean or a property of a bean. Our example uses the latter method. Most of the code indoStartTag
is concerned with constructing an iterator over the collection object. The method first checks if the handler's collection property is set and, if not, proceeds to checking the bean and property attributes. If thename
andproperty
attributes are both set,doStartTag
calls a utility method that uses JavaBeans introspection methods to retrieve the collection. Once the collection object is determined, the method constructs the iterator.If the iterator contains more elements,
doStartTag
sets the value of the scripting variable to the next element and then indicates that the body should be evaluated; otherwise it ends the iteration by returningSKIP_BODY
.After the body has been evaluated, the
doAfterBody
method retrieves the body content and writes it to the out stream. The body content object is then cleared in preparation for another body evaluation. If the iterator contains more elements,doAfterBody
again sets the value of the scripting variable to the next element and returnsEVAL_BODY_AGAIN
to indicate that the body should be evaluated again. This causes the reexecution ofdoAfterBody
. When there are no remaining elements,doAfterBody
terminates the process by returningSKIP_BODY
.public class IterateTag extends TagSupport { protected Iterator iterator = null; protected Object collection = null; protected String id = null; protected String name = null; protected String property = null; protected String type = null; public int doStartTag() throws JspException { Object collection = this.collection; if (collection == null) { try { Object bean = pageContext.findAttribute(name); if (bean == null) { ... throw an exception } if (property == null) collection = bean; else collection = PropertyUtils. getProperty(bean, property); if (collection == null) { ... throw an exception } } catch ... catch exceptions thrown by PropertyUtils.getProperty } } // Construct an iterator for this collection if (collection instanceof Collection) iterator = ((Collection) collection).iterator(); else if (collection instanceof Iterator) iterator = (Iterator) collection; ... } // Store the first value and evaluate, // or skip the body if none if (iterator.hasNext()) { Object element = iterator.next(); pageContext.setAttribute(id, element); return (EVAL_BODY_AGAIN); } else return (SKIP_BODY); } public int doAfterBody() throws JspException { if (bodyContent != null) { try { JspWriter out = getPreviousOut(); out.print(bodyContent.getString()); bodyContent.clearBody(); } catch (IOException e) { ... } } if (iterator.hasNext()) { Object element = iterator.next(); pageContext.setAttribute(id, element); return (EVAL_BODY_AGAIN); } else return (SKIP_BODY); } } }Tag Extra Info Class
Information about the scripting variable is provided in the
IterateTei
tag extra info class. The name and class of the scripting variable are passed in as tag attributes and used to fill in theVariableInfo
constructor.public class IterateTei 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) }; } }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 with 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 could 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
, andparameter
--to define a table of screen definitions and uses aninsert
tag to insert parameters from a screen definition into a specific application screen.JSP Page
The template for the Duke's Bookstore example,
template.jsp
, is shown below. This page includes a JSP page that creates the screen definition and then uses theinsert
tag to insert parameters from the definition into the application screen.<%@ taglib uri="/tutorial-template.tld" prefix="tt" %> <%@ page errorPage="errorpage.jsp" %> <%@ include file="screendefinitions.jsp" %><html> <head> <title> <tt:insert definition="bookstore" parameter="title"/> </title> </head> <tt:insert definition="bookstore" parameter="banner"/> <tt:insert definition="bookstore" parameter="body"/> </body> </html>
screendefinitions.jsp
creates a screen definition based on a request attributeselectedScreen
:<tt:definition name="bookstore" screen="<%= (String)request. getAttribute(\"selectedScreen\") %>"> <tt:screen id="/enter"> <tt:parameter name="title" value="Duke's Bookstore" direct="true"/> <tt:parameter name="banner" value="/banner.jsp" direct="false"/> <tt:parameter name="body" value="/bookstore.jsp" direct="false"/> </tt:screen> <tt:screen id="/catalog"> <tt:parameter name="title" value="<%=messages.getString("TitleBookCatalog")%>" direct="true"/> ... </tt:definition>The template is instantiated by the
Dispatcher
servlet.Dispatcher
first gets the requested screen and stores it as an attribute of the request. This is necessary because when the request is forwarded totemplate.jsp
, the request URL doesn't contain the original request (for example,/bookstore3/catalog
) but instead reflects the path (/bookstore3/template.jsp
) of the forwarded page. Finally, the servlet dispatches the request totemplate.jsp
:public class Dispatcher extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("selectedScreen", request.getServletPath()); RequestDispatcher dispatcher = request.getRequestDispatcher("/template.jsp"); if (dispatcher != null) dispatcher.forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("selectedScreen", request.getServletPath()); RequestDispatcher dispatcher = request.getRequestDispatcher("/template.jsp"); if (dispatcher != null) dispatcher.forward(request, response); } }Tag Handlers
The template tag library contains four tag handlers--
DefinitionTag
,ScreenTag
,ParameterTag
, andInsertTag
--that demonstrate the use of cooperating tags.DefinitionTag
,ScreenTag
, andParameterTag
comprise a set of nested tag handlers that share public and private objects.DefinitionTag
creates a public object nameddefinition
that is used byInsertTag
.In
doStartTag
,DefinitionTag
creates a public object namedscreens
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.public int doStartTag() { HashMap screens = null; screens = (HashMap) pageContext.getAttribute("screens", pageContext.APPLICATION_SCOPE); if (screens == null) pageContext.setAttribute("screens", new HashMap(), pageContext.APPLICATION_SCOPE); return EVAL_BODY_INCLUDE; }The table of screen definitions is filled in by
ScreenTag
andParameterTag
from text provided as attributes to these tags. Table 15-6 shows the contents of the screen definitions hash table for the Duke's Bookstore applicationIn
doEndTag
,DefinitionTag
creates a public object of classDefinition
, selects a screen definition from thescreens
object based on the URL passed in the request, and uses it to initialize theDefinition
object.public int doEndTag()throws JspTagException { try { Definition definition = new Definition(); HashMap screens = null; ArrayList params = null; TagSupport screen = null; screens = (HashMap) pageContext.getAttribute("screens", pageContext.APPLICATION_SCOPE); if (screens != null) params = (ArrayList) screens.get(screenId); else ... if (params == null) ... Iterator ir = null; if (params != null) ir = params.iterator(); while ((ir != null) && ir.hasNext()) definition.setParam((Parameter) ir.next()); // put the definition in the page context pageContext.setAttribute( definitionName, definition); } catch (Exception ex) { ex.printStackTrace(); } return EVAL_PAGE; }If the URL passed in the request is /
enter
, theDefinition
contains the items from the first row of Table 15-6:
Title
Banner
Body
Duke's Bookstore
/banner.jsp
/bookstore.jsp
The definition for the URL
/enter
is shown in Table 15-7. The definition specifies that the value of theTitle
parameter, Duke's Bookstore, should be inserted directly into the output stream, but the values ofBanner
andBody
should be dynamically included.
Table 15-7 Screen Definition for the URL /enter Parameter Name
Parameter Value
isDirect
title
Duke's Bookstore
true
banner
/banner.jsp
false
body
/bookstore.jsp
false
InsertTag
usesDefinition
to insert parameters of the screen definition into the response. In thedoStartTag
method, it retrieves the definition object from the page context.public int doStartTag() { // get the definition from the page context definition = (Definition) pageContext. getAttribute(definitionName); // get the parameter if (parameterName != null && definition != null) parameter = (Parameter)definition. getParam(parameterName); if (parameter != null) directInclude = parameter.isDirect(); return SKIP_BODY; }The
doEndTag
method 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 int doEndTag()throws JspTagException { try { if (directInclude && parameter != null) pageContext.getOut().print(parameter.getValue()); else { if ((parameter != null) && (parameter.getValue() != null)) pageContext.include(parameter.getValue()); } } catch (Exception ex) { throw new JspTagException(ex.getMessage()); } return EVAL_PAGE; }How Is a Tag Handler Invoked?
The
Tag
interface defines the basic protocol between a tag handler and a JSP page's servlet. It defines the life cycle and the methods to be invoked when the start and end tags are encountered.The JSP page's servlet invokes the
setPageContext
,setParent
, and attribute setting methods before callingdoStartTag
. The JSP page's servlet also guarantees thatrelease
will be invoked on the tag handler before the end of the page.Here is a typical tag handler method invocation sequence:
ATag t = new ATag(); t.setPageContext(...); t.setParent(...); t.setAttribute1(value1); t.setAttribute2(value2); t.doStartTag(); t.doEndTag(); t.release();The
BodyTag
interface extendsTag
by defining additional methods that let a tag handler access its body. The interface provides three new methods:
setBodyContent
--Creates body content and adds to the tag handlerdoInitBody
--Called before evaluation of the tag bodydoAfterBody
--Called after evaluation of the tag bodyA typical invocation sequence is:
t.doStartTag(); out = pageContext.pushBody(); t.setBodyContent(out); // perform any initialization needed after body content is set t.doInitBody(); t.doAfterBody(); // whiledoAfterBody
returnsEVAL_BODY_BUFFERED
we // iterate body evaluation ... t.doAfterBody(); t.doEndTag(); t.pageContext.popBody(); t.release();
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.