Iterative Tags

One of the problems with processing dynamic data is that HTML and JSP tags do not support repetitive data very well. There is no way of defining the layout of one row of data and asking for this to be applied to all subsequent rows; each row has to be defined explicitly in the page.

Iterative custom tags interact with the processing of the start and end tags to ask for the tag body to be processed again, and again, and again….

An iterative tag must implement the IterationTag interface. This is most commonly achieved by sub-classing the BodyTagSupport class. The doAfterBody() method must return IterationTag.EVAL_BODY_AGAIN to process the body again or Tag.SKIP_BODY to stop the iteration. Typically, the doAfterBody() method will change the data for each iteration loop.

The iteration tag has complete control over the body content of the page because it can return values from the page interaction methods, such as doAfterBody() and then tell the JSP processor how to continue processing the page.

The default behavior for the BodyTagSupport class is to buffer up the body text of the custom tag and discard it when the end tag is processed. You will have to override this behavior in your custom tag so that it outputs the body text either every time round the iteration loop or once at the end of the tag.

The BodyTagSupport class stores the body content in a BodyTagSupport class instance variable called bodyContent (class javax.servlet.jsp.tagext.BodyContent). The BodyContent class extends the JSPWriter class.

The following code illustrates the normal approach to writing the buffered body content to the Web page:

JspWriter out = getPreviousOut();
out.print(bodyContent.getString());
bodyContent.clearBody();

The steps required are as follows:

1.
Obtain the JSPWriter object that can be used to output the body content text (the getPreviousOut() method).

2.
Print the string data buffered up in the body content.

3.
Clear the body content text after it has been written to the page; otherwise, it will be written more than once.

Typically, the body content is added to the page from within the doAfterBody() method. This keeps the size of the body content down because it is flushed to the page with each iteration and saves on memory usage. If the body content cannot be determined until all of the iterations have completed (or the tag is not an iterative one), you will have to write it to the page in the doEndTag() method.

Listing 14.9 revisits the case study and shows a custom tag for supporting iteration over a customer's jobs.

Listing 14.9. Full Text of ForEachJobTag.java
 1: package web;
 2:
 3: import java.rmi.*;
 4: import javax.ejb.*;
 5: import javax.naming.*;
 6: import javax.servlet.jsp.*;
 7: import javax.servlet.jsp.tagext.*;
 8: import agency.*;
 9:
10: public class ForEachJobTag extends BodyTagSupport {
11:     private Advertise customer;
12:     AdvertiseJobHome advertiseJobHome;
13:     private String[] jobs;
14:     private int nextJob;
15:
16:     public Advertise getCustomer() {
17:         return customer;
18:     }
19:
20:     public void setCustomer(Advertise customer) {
21:         this.customer = customer;
22:     }
23:
24:     public int doStartTag() throws JspException {
25:         try {
26:            InitialContext ic = new InitialContext();
27:            advertiseJobHome = (AdvertiseJobHome)ic.lookup( "java:comp/env/ejb
/AdvertiseJob");
28:            jobs = customer.getJobs();
29:            int nextJob = 0;
30:            return getNextJob();
31:         }
32:         catch (Exception ex) {
33:             throw new JspTagException("ForEachJobTag: "+ex);
34:         }
35:      }
36:
37:     public int doAfterBody() throws JspException {
38:         try {
39:             JspWriter out = getPreviousOut();
40:             out.print(bodyContent.getString());
41:             bodyContent.clearBody();
42:             return getNextJob();
43:         }
44:         catch (Exception ex) {
45:             throw new JspTagException("ForEachJobTag: "+ex);
46:         }
47:     }
48:
49:     private int getNextJob() throws RemoteException, CreateException
50:     {
51:        if (nextJob >= jobs.length)
52:            return SKIP_BODY;
53:        AdvertiseJob advertiseJob = advertiseJobHome.create( jobs[nextJob++],customer
.getLogin());
54:        pageContext.setAttribute("job", advertiseJob, PageContext.REQUEST_SCOPE);
55:        return EVAL_BODY_AGAIN;
56:     }
57: }
					

In Listing 14.9, the doStartTag() and doAfterBody() methods both call the private helper method getNextJob(). It is this method that loads the information about the next job and returns EVAL_BODY_AGAIN while there is still job information available. The value SKIP_BODY is returned to stop the iteration loop when the data is exhausted.

As in the previous GetCustTag shown in Listing 14.7, the ForEachJobTag creates a scripting variable to hold the data for use within the body of the iteration tag. In this case, the variable is always called job and refers to an AdvertiseJob Session bean.

An iteration tag must process the tag body, so the TLD tag entry must define the <body-content> component as JSP to show that the body contains normal JSP text. The attribute passed into the ForEachJob tag to control the iteration is called customer and is a reference to an Advertise Session bean (which defines the customer details). The TLD entry for ForEachJobTag is shown in Listing 14.10.

Listing 14.10. TLD Entry for the ForEachJobTag Tag
 1: <tag>
 2:     <name>forEachJob</name>
 3:     <tag-class>web.ForEachJobTag</tag-class>
 4:     <body-content>JSP</body-content>
 5:     <variable>
 6:       <name-given>job</name-given>
 7:       <variable-class>agency.AdvertiseJob</variable-class>
 8:       <declare>true</declare>
 9:       <scope>AT_BEGIN</scope>
10:     </variable>
11:     <attribute>
12:       <name>customer</name>
13:       <required>true</required>
14:       <rtexprvalue>true</rtexprvalue>
15:       <type>agency.Advertise</type>
16:     </attribute>
17: </tag>
					

With this new tag, you can update the advertise.jsp page to process the jobs without resorting to coding the iteration loop as a Java scriptlet. The following code shows the relevant part of the advertise.jsp page you developed on Day 13.

<H2>Jobs</H2>
<% String[] jobs = cust.getJobs(); %>
<% for (int i=0; i<jobs.length; i++) {%>
  <jsp:useBean id="job" class="web.JobBean" scope="request">
    <jsp:setProperty name="job" property="customer" param="customer"/>
  </jsp:useBean>
  <% job.setRef(jobs[i]); %>
  <H3><jsp:getProperty name="job" property="ref"/></H3>
  <FORM action=updateJob>
  ...
  </FORM>
<% } %>

Here, initialization of the JobBean was awkward because a job has a compound key made up of the customer login name and the job reference. The initialization of the bean set the customer reference as a bean property using the <jsp:setProperty> tag and called the bean's setRef() method directly to reload the bean with the new job details.

You can replace this inelegant approach with much neater and cleanly encapsulated code as shown next:

<H2>Jobs</H2>
<agency:forEachJob customer="<%=cust%>">
  <H3><jsp:getProperty name="job" property="ref"/></H3>
  <FORM action=updateJob>
  ...
  </FORM>
</agency:forEachJob>

The forEachJob tag passes in a reference to the Advertise Session bean using the customer attribute. This attribute is initialized to the scripting variable you defined in the getCust tag in the previous section.

The last improvement you will make to this Web page is to replace the handling of the job skills with a set of cooperating tags, 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
18.227.111.197