Co-operating Tags

Cooperating tags are those that share information in some way. JSP information can be shared using the following mechanisms:

  • Scripting variables

  • Hierarchical (or nested) tags

Using Shared Scripting Variables

One means of writing cooperating tags is to use scripting variables to pass information between the tags. A tag can create a scripting variable that can be retrieved by a another tag on the current page. Depending on the scope of the scripting variable, it can be passed on to other pages in the same request or to pages in subsequent requests. Variables defined by custom tags have the same scoping rules as other variables defined on the page. Scripting variables are a very flexible means of passing information between tags.

A tag can retrieve a scripting variable from the page context using the getAttribute() method. This method takes the name of the variable as its parameter. An overloaded version of the getAttribute() method takes a second parameter that defines the scope of the variable to retrieve.

The tag needs to know the class of the variable it is retrieving. The following example retrieves a String variable stored under the name "title":

String s = (String)pageContext.getAttribute("title");

Hierarchical Tag Structures

An alternative means of passing information between tags is to use a parent/child (or hierarchical) relationship. The parent (outer tag) contains information that is accessed by a child (inner) tag. The parent tag is often an iterative tag, and the child is used to retrieve information for each iteration.

The advantage of this approach over scripting variables is that the information can only be used in the correct context. The scope of the information can be constrained to the Web page between the start and end tags of the parent tag.

Two static methods are provided in the javax.servlet.jsp.tagext.TagSupport class for finding a parent tag from within the child:

  • TagSupport.findAncestorWithClass(from, class) This method searches through the tag hierarchy until it finds a tag with the same class as the second parameter. The first parameter defines the start point of the search and is typically the this object.

  • TagSupport.getParent() This method finds the immediately enclosing parent tag.

With this information, you can now redesign the advertise.jsp to remove the rest of the Java scriptlets from the Job Agency Web pages as shown in the following examples.

On the advertise.jsp page, two separate pages (location.jsp and skills.jsp) handled the job location and skills. Using cooperating tags, you can remove all the messy handling of the HTML select tags. The code for handling the job skills select list currently looks like the following:

<% String[] skills = job.getSkills(); %>
<%@include file="skills.jsp"%>

The code creates a Java variable called skills that is used by the skills.jsp page shown in Listing 14.11.

Listing 14.11. Full Text of skills.jsp
 1: <SELECT name="skills" multiple size="6">
 2: <%
 3:   Iterator allSkills = agency.getSkills().iterator();
 4:   while (allSkills.hasNext()) {
 5:     String s = (String)allSkills.next();
 6:     boolean found = false;
 7:     for (int si=0; !found && si<skills.length; si++)
 8:         found = s.equals(skills[si]);
 9:     if (found)
10:         out.print("<OPTION selected>");
11:     else
12:         out.print("<OPTION>");
13:     out.print(s);
14:   }
15: %>
16: </SELECT>
						

The use of the Java variable to communicate between the two pages allowed the register.jsp page to use the same skills.jsp code to generate the applicant's skill list. This is an error-prone approach as the skills.jsp page will not work unless this variable is defined before the skills.jsp page is included.

In other words the skills.jsp page is not self contained. You will be able to write a much more elegant solution by using cooperating tags.

You will need two custom tags—one to control the iteration over a list of option values and the other to define whether each option is selected or not. Listing 14.12 shows a generic iteration tag (called ForEachTag) that takes a collection of strings to iterate over.

Listing 14.12. Full Text of ForEachTag.java
 1: package web;
 2:
 3: import java.io.*;
 4: import java.util.*;
 5: import javax.servlet.jsp.*;
 6: import javax.servlet.jsp.tagext.*;
 7:
 8: public class ForEachTag extends BodyTagSupport {
 9:     private Collection collection;
10:     private Iterator it;
11:     private String currentValue;
12:
13:     public void setCollection (Collection collection) {
14:         this.collection = collection;
15:     }
16:
17:     public Collection getCollection () {
18:         return collection;
19:     }
20:
21:     public int doStartTag() {
22:         it = collection.iterator();
23:         return getNext();
24:      }
25:
26:     public int doAfterBody() throws JspTagException {
27:         try {
28:             JspWriter out = getPreviousOut();
29:             out.print(bodyContent.getString());
30:             bodyContent.clearBody();
31:             return getNext();
32:         }
33:         catch (IOException ex) {
34:             throw new JspTagException("ForEachTag: "+ex);
35:         }
36:     }
37:
38:     private int getNext()
39:     {
40:        if (it.hasNext()) {
41:            currentValue = (String)it.next();
42:            return EVAL_BODY_AGAIN;
43:        }
44:        return SKIP_BODY;
45:     }
46:
47:     public String getCurrentValue() {
48:        return currentValue;
49:     }
50: }
						

This is similar to the previous iteration example with the addition of an accessor method called getCurrentValue() to obtain the current iteration value.

The nested option tag (OptionTag), shown in Listing 14.13, looks up the tag hierarchy using findAncestorWithClass() to find the enclosing ForEachTag object. The inner tag then gets the current iteration value from the found ancestor object and compares it to a list of values passed as an attribute to the inner tag. If the current value is in the supplied list, the option is tagged as a selected option.

Listing 14.13. Full Text of OptionTag.java
 1: package web;
 2:
 3: import java.io.*;
 4: import java.util.*;
 5: import javax.servlet.jsp.*;
 6: import javax.servlet.jsp.tagext.*;
 7:
 8: public class OptionTag extends TagSupport {
 9:     private String[] selected = new String[0];
10:
11:     public void setSelected (String[] selected) {
12:         this.selected = selected;
13:     }
14:
15:     public String[] getSelected () {
16:         return selected;
17:     }
18:
19:     public int doStartTag() throws JspTagException {
20:         try {
21:             ForEachTag loop = (ForEachTag)findAncestorWithClass( this, ForEachTag.class);
22:             String value = loop.getCurrentValue();
23:             for (int i=0; i<selected.length; i++) {
24:                 if (value.equals(selected[i])) {
25:                     pageContext.getOut().print( "<OPTION selected>"+value);
26:                     return SKIP_BODY;
27:                 }
28:             }
29:             pageContext.getOut().print("<OPTION>"+value);
30:         }
31:         catch (IOException ex) {
32:             throw new JspTagException("OptionTag: "+ex);
33:         }
34:         return SKIP_BODY;
35:      }
36:
37: }
						

The OptionTag example is inherently an extension to the HTML OPTION tag, whereas the ForEach example simply iterates over a generic list. Both tags are eminently reusable on other JSP pages.

The TLD entries for these tags are shown in Listing 14.14.

Listing 14.14. TLD entries for ForEachTag and OptionTag
 1: <tag>
 2:     <name>forEach</name>
 3:     <tag-class>web.ForEachTag</tag-class>
 4:     <body-content>JSP</body-content>
 5:     <attribute>
 6:       <name>collection</name>
 7:       <required>true</required>
 8:       <rtexprvalue>true</rtexprvalue>
 9:       <type>java.util.Collection</type>
10:     </attribute>
11:   </tag>
12:   <tag>
13:     <name>option</name>
14:     <tag-class>web.OptionTag</tag-class>
15:     <body-content>empty</body-content>
16:     <attribute>
17:       <name>selected</name>
18:       <required>false</required>
19:       <rtexprvalue>true</rtexprvalue>
20:       <type>java.util.String[]</type>
21:     </attribute>
22: </tag>
						

Note how the selected attribute of the option tag is an array of String objects and is optional. The code in the tag implementation defaults this attribute to an empty array if it is not specified; effectively, none of the select options will be selected.

You can now complete the refactoring of the advertise.jsp page by using the cooperating tags. The new tag attributes are defined using Java expressions. The only complication is the need to convert the single location string into an array. The following code highlights how clean the new code for handling the location is.

The following fragment shows the code you developed on Day 13 to define the list of available locations:

<TR><TD>Location:</TD><TD>
  <% String location = job.getLocation(); %>
  <%@include file="location.jsf"%>
</TD></TR>

The included file location.jsf defines the SELECT list for the location as follows:

<SELECT name="location">
<%
  Iterator locations = agency.getLocations().iterator();
  while (locations.hasNext()) {
    String l = (String)locations.next();
    if (location == l)
      out.print("<OPTION>");
    else
      out.print("<OPTION selected>");
    out.print(l);
  }
%>
</SELECT>

You can replace this convoluted approach with a neater version using the two custom tags just shown:

<SELECT name="location">
  <agency:forEach collection='<%=agency.getLocations()%>'>
    <agency:option selected='<%=new String[]{job.getLocation()}%>'/>
  </agency:forEach>
</SELECT>

The complete version of the revised Advertise Job Web page is shown in Listing 14.15.

Listing 14.15. Full Text of advertise.jsp
 1: <%@ taglib uri="/agency" prefix="agency" %>
 2: <HTML><HEAD>
 3: <TITLE>Advertise Customer Details</TITLE>
 4: <%@include file="header.jsp" %>
 5: <%@page import="java.util.*" %>
 6: <agency:getCust login='<%=request.getParameter("customer")%>'/>
 7: <H2>Customer details for: <jsp:getProperty name="cust" property="login"/></H2>
 8: <FORM action=updateCustomer>
 9: <INPUT type=hidden name=login value="<jsp:getProperty name="cust" property="login"/>">
10: <TABLE>
11:   <TR><TD>Login:</TD><TD><jsp:getProperty name="cust" property="login"/></TD></TR>
12:   <TR><TD>Name:</TD><TD><input type=text name=name value= "<jsp:getProperty
 name="cust" property="name"/>"></TD></TR>
13:   <TR><TD>Email:</TD><TD><input type=text name=email value= "<jsp:getProperty
 name="cust" property="email"/>"></TD></TR>
14:   <% String[] address = cust.getAddress(); %>
15:   <TR><TD>Address:</TD><TD> <input type=text name=address value="<%=address[0]%>"></TD></TR>
16:   <TR><TD>Address:</TD> <TD><input type=text name=address value="<%=address[1]%>"></TD></TR>
17: </TABLE>
18: <INPUT type=submit value="Update Details">
19: <INPUT type=reset>
20: </FORM>
21: <FORM action=deleteCustomer>
22: <input type=hidden name=customer value="<jsp:getProperty name= "cust" property="login"/>">
23: <input type=submit value="Delete Customer <jsp:getProperty name= "cust"
 property="login"/>">
24: </FORM>
25: <H2>Jobs</H2>
26: <agency:forEachJob customer="<%=cust%>">
27:   <H3><jsp:getProperty name="job" property="ref"/></H3>
28:   <FORM action=updateJob>
29:     <TABLE>
30:       <TR><TD>Description:</TD><TD> <input type=text name=description value="<jsp
:getProperty name="job" property="description"/>"></TD></TR>
31:       <TR><TD>Location:</TD>
32:         <TD>
							33:         <SELECT name="location">
							34:           <agency:forEach collection='<%=agency.getLocations()%>'>
							35:             <agency:option selected='<%=new String[]{job.getLocation()} %>'/>
							36:           </agency:forEach>
							37:         </SELECT>
38:         </TD>
39:       </TR>
40:       <TR><TD>Skills:</TD><TD>
41:       <% String[] skills = job.getSkills(); %>
42:       <SELECT name=skills multiple size=6>
							43:       <agency:forEach collection='<%=agency.getSkills()%>'>
							44:         <agency:option selected='<%=job.getSkills()%>'/>
							45:       </agency:forEach>
							46:       </SELECT>
47:       </TD></TR>
48:     </TABLE>
49:     <INPUT type=hidden name="customer" value="<jsp:getProperty name="job"
 property="customer"/>">
50:     <INPUT type=hidden name="ref" value="<jsp:getProperty name="job" property="ref"/>">
51:     <INPUT type=submit value="Update Job">
52:   </FORM>
52:   <FORM action=deleteJob>
54:     <INPUT type=hidden name="customer" value="<jsp:getProperty name="job"
 property="customer"/>">
55:     <INPUT type=hidden name="ref" value="<jsp:getProperty name="job" property="ref"/>">
56:     <INPUT type="submit" value='Delete Job <jsp:getProperty name="job" property="ref"/>'>
57:   </FORM>
58: </agency:forEachJob>
59: <H2>Create New Job</H2>
60: <FORM action=createJob>
61: <TABLE>
62: <TR>
63:   <TD>Ref:</TD>
64:   <TD><INPUT type=text name=ref></TD>
65: </TR>
66: <TR>
67:   <INPUT type=hidden name="customer" value="<jsp:getProperty name="cust"
 property="login"/>">
68:   <TD colspan=2><INPUT type=submit value="Create Job"></TD>
69: </TR>
70: </TABLE>
71: </FORM>
72: <%@include file="footer.jsp" %>
73: </BODY>
74: </HTML>
						

The ForEach tag was designed to be useful for any kind of iteration of values to show you how generic tags can be developed. In this example, it may have been better to have an iteration tag that implemented the HTML SELECT list rather than require the developer to define them on the JSP page. Such a custom tag would need to have attributes matching the attributes of the HTML SELECT tag. The JSP code using a custom <select> tag could look like the following:

<agency:select name="skills" multiple="true" size="6" collection="<%=agency.getSkills()%>">
   <agency:option selected="<%=job.getSkills()%>"/>
</agency:select>

One of the problems with more complex custom tags is that there are often restrictions on how the attributes can be defined that can't be shown in TLD definition. A tag may require that an attribute have specific values (the prevous size tag must be a positive integer) or perhaps two tags are mutually exclusive. Custom tags address this problem by using a Tag Extra Info (TEI) object, as discussed in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.60.63