12.3. Looping with the forEach Tag

Perhaps the single most important tag in JSTL is forEach. It provides the ability to loop with explicit numeric values or to iterate down an array, Collection, Iterator, Enumeration, Map, ResultSet, or comma-separated String. The forEach tag also lets you access an IteratorTagStatus object that provides a variety of information about the loop status.

The forEach tag has six available attributes: var, begin, end, step, items, and status. The following list summarizes their use; the following subsections give details and examples.

  • var. The iteration variable. When you loop with explicit numeric values, the variable contains the value of the loop index. When you iterate over data structures, the variable contains the individual value from within the data structure.

  • begin. When you loop with explicit numeric values, this attribute gives the initial value of the loop index. When you loop down a data structure, this attribute gives the index of the first item that should be accessed.

  • end. When you loop with explicit numeric values, this attribute gives the final value of the loop index. When you loop down a data structure, this attribute gives the index of the last item that should be accessed.

  • step. This attribute gives the size of the loop index increment.

  • items. This attribute supplies the value of the data structure to iterate over. It is not used when you loop with explicit numeric values.

  • status. This attribute gives the name of a variable that will hold an IteratorTagStatus object that provides details on the loop status.

Looping with Explicit Numeric Values

The simplest type of loop is one in which you specify only var, begin, and end attributes. The var attribute defines the loop index variable; begin and end give the initial and final values of the variable.

With the jr library, you access the loop index by retrieving the PageContext attribute named by the var attribute. For example, the following code outputs a bulleted list containing the numbers 1 through 10.

<UL> 
<jr:forEach var="i" begin="1" end="10"> 
  <LI><%= pageContext.getAttribute("i") %>
							</jr:forEach> 
</UL> 

With the jx library, you can access attributes by using a dollar sign followed by the attribute name. To simply output an attribute, you use the expr tag along with its value attribute. So, for example, the following jx code outputs the same bulleted list as the jr code just shown.

<UL> 
<jx:forEach var="i" begin="1" end="10"> 
  <LI><jx:expr value="$i"/>
							</jx:forEach> 
</UL> 

Listing 12.3 shows a JSP page that uses the jr code; Figure 12-1 shows the result. Listing 12.4 shows a JSP page that uses the jx code; Figure 12-2 shows the result. Listing 12.5 shows the deployment descriptor that both pages require.

Figure 12-1. Result of simple-loop-jr.jsp.


Figure 12-2. Result of simple-loop-jx.jsp.


Listing 12.3. simple-loop-jr.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Simple Loop: "jr" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Simple Loop: "jr" Version 
</TABLE> 
<P> 
<!-- Note that the uri refers to the location defined by 
     the taglib element of web.xml, not to the real 
     location of the TLD file. --> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jr" prefix="jr" %> 
<UL> 
<jr:forEach var="i" begin="1" end="10"> 
  <LI><%= pageContext.getAttribute("i") %>
							</jr:forEach> 
</UL> 
</BODY> 
</HTML> 

Listing 12.4. simple-loop-jx.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Simple Loop: "jx" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Simple Loop: "jx" Version 
</TABLE> 
<P> 
<!-- Note that the uri refers to the location defined by 
     the taglib element of web.xml, not to the real 
     location of the TLD file. --> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jx" prefix="jx" %> 
<UL> 
<jx:forEach var="i" begin="1" end="10"> 
  <LI><jx:expr value="$i"/>
							</jx:forEach> 
</UL> 
</BODY> 
</HTML> 

Listing 12.5. web.xml (Excerpt for simple-loop examples)
<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE web-app PUBLIC 
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
    "http://java.sun.com/dtd/web-app_2_3.dtd"> 

<web-app> 
  <!-- Use the SPEL for expressions. --> 
  <context-param>
							<param-name>
							javax.servlet.jsptl.ExpressionEvaluatorClass
							</param-name>
							<param-value>
							org.apache.taglibs.jsptl.lang.spel.Evaluator
							</param-value>
							</context-param> 

  <!-- If URL gives a directory but no filename, try index.jsp 
       first and index.html second. If neither is found, the 
       result is server specific (e.g., a directory listing). 
       Order of elements in web.xml matters. welcome-file-list 
       needs to come after servlet but before error-page. 
  --> 
  <welcome-file-list> 
    <welcome-file>index.jsp</welcome-file> 
    <welcome-file>index.html</welcome-file> 
  </welcome-file-list> 

  <!-- Register jr JSTL TLD file. --> 
  <taglib>
							<taglib-uri>
							http://java.sun.com/jsptl/ea/jr
							</taglib-uri>
							<taglib-location>
							/WEB-INF/jsptl-tlds/jsptl-jr.tld
							</taglib-location>
							</taglib> 

  <!-- Register jx JSTL TLD file. --> 
  <taglib>
							<taglib-uri>
							http://java.sun.com/jsptl/ea/jx
							</taglib-uri>
							<taglib-location>
							/WEB-INF/jsptl-tlds/jsptl-jx.tld
							</taglib-location>
							</taglib> 
</web-app> 

Looping with a Designated Step Size

The forEach tag permits you to use the step attribute to specify how much the loop index will increase each time around the loop. For example, suppose that you want to generate a bulleted list that shows the number of seconds every minute from zero until the session timeout.

With the jr library, this task is straightforward. Simply supply the value of session.getMaxInactiveInterval for the end attribute and 60 for the step attribute, as shown in the following example.

<UL> 
<jr:forEach var="seconds" 
            begin="0" 
            end="<%= session.getMaxInactiveInterval() %>"
							step="60"> 
  <LI><%= pageContext.getAttribute("seconds") %> seconds. 
</jr:forEach> 
  <LI>Timeout exceeded. 
</UL> 

With the jx library, however, there is a problem. The end attribute does not permit a JSP expression. Furthermore, since session is an implicit object (predefined variable), not an attribute of PageContext, you cannot use the $ notation to access it. (However, the JSR-052 group is considering providing simplified access to the implicit objects in the final version of JSTL.) Now, as we will see in Section 12.7 (Using the Expression Language), the jx library’s set tag can be used to assign an attribute based on a JSP expression. However, that approach is not satisfactory since one of the purposes of the jx library is to avoid explicit Java code in the JSP page. So, I create a custom JSP tag that does nothing but store the PageContext object in the pageContext attribute of the PageContext object (i.e., of itself!). After this custom tag is used, the PageContext object can be accessed in the jx library with $pageContext. The various implicit objects can be accessed with $pageContext.request (i.e., by calling getRequest on the PageContext object), $pageContext.response (i.e., by calling getResponse on the PageContext object), $pageContext.session (i.e., by calling getSession on the PageContext object), etc. So, after this custom tag is used, the following jx code yields the same result as the jr code just shown.

<UL> 
<jx:forEach var="seconds" 
            begin="0" 
            end="$pageContext.session.maxInactiveInterval"
							step="60"> 
  <LI><jx:expr value="$seconds"/> seconds. 
</jx:forEach> 
  <LI>Timeout exceeded. 
</UL> 

Listing 12.6 shows a JSP page that uses the jr code; Figure 12-3 shows the result. Listing 12.7 shows a JSP page that uses the new custom tag and the jx code; Figure 12-4 shows the result. Listings 12.8 and 12.9 show the custom tag and associated TLD file for a tag that stores the PageContext object in the pageContext attribute. Listing 12.5 (shown on page 633) presents the deployment descriptor that both pages require.

Figure 12-3. Result of inactive-interval-loop-jr.jsp.


Figure 12-4. Result of inactive-interval-loop-jx.jsp.


Listing 12.6. inactive-interval-loop-jr.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Session Timeout: "jr" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Session Timeout: "jr" Version 
</TABLE> 
<P> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jr" prefix="jr" %> 
<UL> 
<jr:forEach var="seconds"
							begin="0"
							end="<%= session.getMaxInactiveInterval() %>"
							step="60"> 
  <LI><%= pageContext.getAttribute("seconds") %> seconds. 
</jr:forEach> 
  <LI>Timeout exceeded. 
</UL> 
</BODY> 
</HTML> 

Listing 12.7. inactive-interval-loop-jx.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Session Timeout: "jx" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Session Timeout: "jx" Version 
</TABLE> 
<P> 
<%@ taglib uri="/WEB-INF/store-page-context.tld" prefix="init" %>
							<init:storePageContext/>
							<%@ taglib uri="http://java.sun.com/jsptl/ea/jx" prefix="jx" %> 
<UL> 
<jx:forEach var="seconds"
							begin="0"
							end="$pageContext.session.maxInactiveInterval"
							step="60"> 
  <LI><jx:expr value="$seconds"/> seconds. 
</jx:forEach> 
  <LI>Timeout exceeded. 
</UL> 
</BODY> 
</HTML> 

Listing 12.8. StorePageContextTag.java
package moreservlets.tags; 

import javax.servlet.*; 
import javax.servlet.jsp.*; 
import javax.servlet.jsp.tagext.*; 
import java.io.*; 

/** Stores the PageContext reference in an attribute 
 *  of the PageContext. The reason for doing this is to 
 *  make it easier for JSTL jx tags to access the request, 
 *  response, pageContext, etc. 
 */ 

public class StorePageContextTag extends TagSupport {
  public int doStartTag() {
    pageContext.setAttribute("pageContext", pageContext); 
    return(SKIP_BODY); 
  } 
} 

Listing 12.9. store-page-context.tld
<?xml version="1.0" encoding="ISO-8859-1" ?> 
<!DOCTYPE taglib 
 PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
 "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> 

<taglib> 
  <tlib-version>1.0</tlib-version> 
  <jsp-version>1.2</jsp-version> 
  <short-name>store-page-context</short-name> 
  <description> 
    An extremely simple tag that simply stores a reference 
    to the PageContext object in a PageContext attribute. 
    This setting is performed because the JSTL jx library 
    makes it easy to access attributes but does not make it 
    easy to access scripting variables like request, response, 
    pageContext, etc. 
  </description> 
  <!-- Define a tag the stores the pageContext attribute. --> 
  <tag>
							<name>storePageContext</name>
							<tag-class>
							moreservlets.tags.StorePageContextTag
							</tag-class>
							<body-content>empty</body-content>
							<description>Store PageContext in attribute.</description>
							</tag> 
</taglib> 

Looping Down Arrays

The previous examples used the begin and end attributes of forEach to specify explicit numeric values for looping. Even more useful is the ability to iterate over a data structure. To do this, you supply an array, Collection, Iterator, Enumeration, Map, ResultSet, or comma-separated String as the value of the items attribute. The var attribute gives the name of the PageContext attribute that will store each individual item from the data structure. It is also legal to use begin, end, and step so that only some of the items are accessed.

For example, the following jr code creates a table of the cookies that were sent by the client on the current request.

<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Cookie Name 
      <TH CLASS="HEADING">Cookie Value 
<jr:forEach var="cookie" 
            items="<%= request.getCookies() %>"> 
  <% Cookie cookie = 
       (Cookie)pageContext.getAttribute("cookie"); %> 
  <TR><TD><%= cookie.getName() %> 
      <TD><%= cookie.getValue() %> 
</jr:forEach> 
</TABLE> 

Given the custom tag that stores the PageContext object in the pageContext attribute (see Listings 12.8 and 12.9), the following jx code has the same effect as the jr code just shown. Recall that “ $ ” is used to access attributes and “ . ” is used to access bean properties. So, for example, $pageContext.request.cookies means that the system should retrieve the object stored in the pageContext attribute (i.e., the PageContext object itself), call getRequest on it, then call getCookies on that result.

<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Cookie Name 
      <TH CLASS="HEADING">Cookie Value 
<jx:forEach var="cookie" 
            items="$pageContext.request.cookies"> 
  <TR><TD><jx:expr value="$cookie.name"/> 
      <TD><jx:expr value="$cookie.value"/> 
</jx:forEach> 
</TABLE> 

Listing 12.10 shows a JSP page that uses the jr code; Figure 12-5 shows the result after a cookie-setting servlet (Listing 12.12) is accessed. Listing 12.11 shows a JSP page that uses the jx code; Figure 12-6 shows the result after the cookie-setting servlet (Listing 12.12) is accessed. Listing 12.13 shows the deployment descriptor needed by the JSP pages and the cookie-setting servlet.

Figure 12-5. Result of cookie-loop-jr.jsp after the DefineCookies servlet (Listing 12.12) is visited.


Figure 12-6. Result of cookie-loop-jx.jsp after the DefineCookies servlet (Listing 12.12) is visited.


Note that both pages fail if no cookies are sent. We don’t yet have the tools to fix this problem; however, in Section 12.7 (Using the Expression Language), you’ll see how to use the expr element to supply default values for missing items. Also, in Section 12.6 (Evaluating Items Conditionally) you’ll see how to do general conditional evaluation.

Listing 12.10. cookie-loop-jr.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Cookies: "jr" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Cookies: "jr" Version 
</TABLE> 
<P> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jr" prefix="jr" %> 
<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Cookie Name 
      <TH CLASS="HEADING">Cookie Value 
<jr:forEach var="cookie"
							items="<%= request.getCookies() %>">
							<% Cookie cookie =
							(Cookie)pageContext.getAttribute("cookie"); %> 
  <TR><TD><%= cookie.getName() %> 
      <TD><%= cookie.getValue() %>
							</jr:forEach> 
</TABLE> 
</BODY> 
</HTML> 

Listing 12.11. cookie-loop-jx.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Cookies: "jx" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Cookies: "jx" Version 
</TABLE> 
<P> 
<%@ taglib uri="/WEB-INF/store-page-context.tld" prefix="init" %>
							<init:storePageContext/>
							<%@ taglib uri="http://java.sun.com/jsptl/ea/jx" prefix="jx" %> 
<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Cookie Name 
      <TH CLASS="HEADING">Cookie Value 
<jx:forEach var="cookie"
							items="$pageContext.request.cookies"> 
  <TR><TD><jx:expr value="$cookie.name"/> 
      <TD><jx:expr value="$cookie.value"/>
							</jx:forEach> 
</TABLE> 
</BODY> 
</HTML> 

Listing 12.12. DefineCookies.java
package moreservlets; 

import java.io.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 
import java.util.*; 

/** Defines a few cookies, then redirects the user 
 *  to the cookie-displaying page. 
 */ 

public class DefineCookies extends HttpServlet {
  public void doGet(HttpServletRequest request, 
                    HttpServletResponse response) 
      throws ServletException, IOException {
    for(int i=0; i<5; i++) {
      Cookie c = new Cookie("cookie" + i, "value" + i);
							response.addCookie(c); 
    } 
    String cookieDisplayPage = 
      request.getContextPath() + "/forEach/cookie-loop-jr.jsp"; 
    response.sendRedirect(cookieDisplayPage); 
  } 
} 

Listing 12.13. web.xml (Excerpt for cookie-displaying pages)
<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE web-app PUBLIC 
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
    "http://java.sun.com/dtd/web-app_2_3.dtd"> 

<web-app> 
  <!-- Use the SPEL for expressions. --> 
  <context-param> 
    <param-name> 
      javax.servlet.jsptl.ExpressionEvaluatorClass 
    </param-name> 
    <param-value> 
      org.apache.taglibs.jsptl.lang.spel.Evaluator 
    </param-value> 
  </context-param> 
  <!-- Give a name to the moreservlets.DefineCookies servlet 
       so that a custom URL can later be assigned. 
  --> 
  <servlet>
							<servlet-name>DefineCookies</servlet-name>
							<servlet-class>moreservlets.DefineCookies</servlet-class>
							</servlet> 

  <!-- Register the URL /forEach/DefineCookies with the 
       DefineCookies servlet. This prevents the servlet 
       from needing to define a path of "/" for the cookies. 
       Instead, the servlet (which sets the cookies) and the 
       JSP pages (which display the cookies) have the same 
       URL prefix, so no special settings are needed. 
  --> 
  <servlet-mapping>
							<servlet-name>DefineCookies</servlet-name>
							<url-pattern>/forEach/DefineCookies</url-pattern>
							</servlet-mapping> 

  <!-- ... --> 

  <!-- Register jr JSTL TLD file. --> 
  <taglib> 
    <taglib-uri> 
      http://java.sun.com/jsptl/ea/jr 
    </taglib-uri> 
    <taglib-location> 
      /WEB-INF/jsptl-tlds/jsptl-jr.tld 
    </taglib-location> 
  </taglib> 

  <!-- Register jx JSTL TLD file. --> 
  <taglib> 
    <taglib-uri> 
      http://java.sun.com/jsptl/ea/jx 
    </taglib-uri> 
    <taglib-location> 
      /WEB-INF/jsptl-tlds/jsptl-jx.tld 
    </taglib-location> 
  </taglib> 
</web-app> 

Looping Down Enumerations

You iterate over a java.util.Enumeration object in exactly the same manner as you iterate over an array: you define the iteration variable with the var attribute of forEach and supply the object with the items attribute.

For example, the getHeaderNames method of HttpServletRequest returns an Enumeration of the request headers sent by the client. So, the following jr code will create a table of the names and values of all headers in the request.

<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Header Name 
      <TH CLASS="HEADING">Header Value 
<jr:forEach var="header" 
            items="<%= request.getHeaderNames() %>"> 
  <% String header = 
       (String)pageContext.getAttribute("header"); %> 
  <TR><TD><%= header %> 
      <TD><%= request.getHeader(header) %> 
</jr:forEach> 
</TABLE> 

Once again, however, we have a problem performing the same task with the jx library. Given the custom tag that defines the pageContext attribute, the Enumeration itself can be accessed with $pageContext.request.headerNames. However, once you have a header name, how do you get a header value? There is no bean property (i.e., zero-argument getXxx method) that gives you a header value, and the jx library provides no way to specify arguments to methods without using Java code. You’re stuck: you have to write a special-purpose custom tag or use scripting expressions. Use of the jx library does not always negate the need for explicit Java code.

Even after you resign yourself to using scripting expressions with the jx library, you’d like to minimize their use. A good way to do this is to use the declare tag. This element is described in Section 12.7, but the gist of it is that declare provides a bridge between jx tags and scripting elements by copying the value of an attribute into a scripting variable. The following jx code uses declare to create the same request header table as the previous jr code.

<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Header Name 
      <TH CLASS="HEADING">Header Value 
<jx:forEach var="headerName" 
            items="$pageContext.request.headerNames"> 
  <TR><TD><jx:expr value="$headerName"/> 
      <TD><jx:declare id="headerName" type="java.lang.String"/>
							<%= request.getHeader(headerName) %> 
</jx:forEach> 
</TABLE> 

Listing 12.14 shows a JSP page that uses the jr code; Figure 12-7 shows the result. Listing 12.15 shows a JSP page that uses the jx code; Figure 12-8 shows the result.

Figure 12-7. Result of header-loop-jr.jsp.


Figure 12-8. Result of header-loop-jx.jsp.


Listing 12.14. header-loop-jr.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Headers: "jr" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Headers: "jr" Version 
</TABLE> 
<P> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jr" prefix="jr" %> 
<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Header Name 
      <TH CLASS="HEADING">Header Value 
<jr:forEach var="header"
							items="<%= request.getHeaderNames() %>">
							<% String header =
							(String)pageContext.getAttribute("header"); %> 
  <TR><TD><%= header %> 
      <TD><%= request.getHeader(header) %>
							</jr:forEach> 
</TABLE> 
</BODY> 
</HTML> 

Listing 12.15. header-loop-jx.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Headers: "jx" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Headers: "jx" Version 
</TABLE> 
<P> 
<%@ taglib uri="/WEB-INF/store-page-context.tld" prefix="init" %>
							<init:storePageContext/>
							<%@ taglib uri="http://java.sun.com/jsptl/ea/jx" prefix="jx" %> 
<TABLE BORDER="1" ALIGN="CENTER"> 
  <TR><TH CLASS="HEADING">Header Name 
      <TH CLASS="HEADING">Header Value 
<jx:forEach var="headerName"
							items="$pageContext.request.headerNames"> 
  <TR><TD><jx:expr value="$headerName"/> 
      <TD><jx:declare id="headerName" type="java.lang.String"/>
							<%= request.getHeader(headerName) %>
							</jx:forEach> 
</TABLE> 
</BODY> 
</HTML> 

Looping Down Entries in a String

In addition to the standard collection data structures (array, Enumeration, Collection, etc.), the standard tag library lets you loop down comma-delimited strings.

For example, the following jr code makes a bulleted list of countries. Note that, since the items attribute can contain objects of various types, you have to use explicit double quotes within the attribute value to designate a String. The easiest way to do this is to use single quotes around the overall value.

<UL> 
<jr:forEach var="country" 
            items='"Australia,Canada,Japan,Philippines,USA"'> 
  <LI><%= pageContext.getAttribute("country") %> 
</jr:forEach> 
</UL> 

The following jx code yields the same result. Since the items attribute cannot accept request time expressions, there is no need to use an extra set of quote marks.

<UL> 
<jx:forEach var="country" 
            items="Australia,Canada,Japan,Philippines,USA"> 
  <LI><jx:expr value="$country"/> 
</jx:forEach> 
</UL> 

Listing 12.16 shows a JSP page that uses the jr code; Figure 12-9 shows the result. Listing 12.17 shows a JSP page that uses the jx code; Figure 12-10 shows the result.

Figure 12-9. Result of string-loop-jr.jsp.


Figure 12-10. Result of string-loop-jx.jsp.


Listing 12.16. string-loop-jr.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>String: "jr" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Strings: "jr" Version 
</TABLE> 
<P> 
<H3>Marty has given servlet and JSP short courses in:</H3> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jr" prefix="jr" %> 
<UL> 
<jr:forEach var="country"
							items='"Australia,Canada,Japan,Philippines,USA"'> 
  <LI><%= pageContext.getAttribute("country") %>
							</jr:forEach> 
</UL> 
For more details or to schedule a short course at <I>your</I> 
company, see <A HREF="http://courses.coreservlets.com"> 
http://courses.coreservlets.com</A>. 
</BODY> 
</HTML> 

Listing 12.17. string-loop-jx.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>String: "jx" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 
<BODY> 
<TABLE BORDER=5 ALIGN="CENTER"> 
  <TR><TH CLASS="TITLE"> 
      Strings: "jx" Version 
</TABLE> 
<P> 
<H3>Marty has given servlet and JSP short courses in:</H3> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jx" prefix="jx" %> 
<UL> 
<jx:forEach var="country"
							items="Australia,Canada,Japan,Philippines,USA"> 
  <LI><jx:expr value="$country"/>
							</jx:forEach> 
</UL> 
For more details or to schedule a short course at <I>your</I> 
company, see <A HREF="http://courses.coreservlets.com"> 
http://courses.coreservlets.com</A>. 
</BODY> 
</HTML> 

Looping Down Multiple Data Types

The items attribute of forEach can specify an array, Collection, Iterator, Enumeration, Map, ResultSet, or comma-separated String. For all cases except Map and ResultSet, the value of the iteration variable is obvious: it is the appropriate entry from the data structure.

Since a Map consists of both names and values, however, what does the iteration variable return? The answer is that it returns a Map.Entry object. The Map.Entry interface, in turn, defines getKey and getValue methods.

The value of the iteration variable when you loop over a ResultSet is even more surprising: it is the ResultSet itself! The reason that this approach is useful is that the system maintains a cursor that refers to the current row of the ResultSet. That cursor is updated each time around the loop.

OK, so I’ve repeatedly claimed that the real strength of the jx library is when it is used to access attributes that are set in a servlet that forwards the request to the JSP page. Let’s test this theory out in a relatively realistic MVC scenario. Listing 12.18 (Figure 12-11) shows an HTML form that collects a variety of data from a user. When the form is submitted, the data is sent to the ShowData servlet (Listing 12.19). This servlet creates an array (the values of the computerLanguages request parameter), an Enumeration (a StringTokenizer created from the values of the spokenLanguages request parameter), a Collection (an ArrayList giving predefined favorite foods), a Map (a HashMap giving predefined usernames and passwords), and a comma-delimited String (usernames of people on probation for bad behavior). The servlet stores all of these values in request attributes and then forwards the request to a jr (Listing 12.20) or jx (Listing 12.21) page to display the results (Figures 12-12 and 12-13). Listing 12.22 shows the deployment descriptor.

Figure 12-11. Result of data-form.html. Data, when submitted, is sent to the ShowData servlet (Listing 12.19). The servlet, in turn, forwards the request to either show-data-jr.jsp (Listing 12.20, Figure 12-12) or show-data-jx.jsp (Listing 12.21, Figure 12-13).


Figure 12-12. Result of the ShowData servlet when it forwards the request to show-data-jr.jsp.


Figure 12-13. Result of the ShowData servlet when it forwards the request to show-data-jx.jsp.


In this case, the jx version is considerably less complicated than the jr version: no scripting expressions are needed, and the code is significantly more concise.

Listing 12.18. data-form.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Languages</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<CENTER> 
<TABLE BORDER=5> 
  <TR><TH CLASS="TITLE"> 
      Languages</TABLE> 
<P> 
<FORM ACTION="ShowData"> 
<H3>Select the computer languages you know:</H3> 
<INPUT TYPE="CHECKBOX" NAME="computerLanguages" 
       value="Java">Java<BR> 
<INPUT TYPE="CHECKBOX" NAME="computerLanguages" 
       value="C++">C++<BR> 
<INPUT TYPE="CHECKBOX" NAME="computerLanguages" 
       value="Common Lisp">Common Lisp<BR> 
<INPUT TYPE="CHECKBOX" NAME="computerLanguages" 
       value="Smalltalk">Smalltalk<BR> 
<INPUT TYPE="CHECKBOX" NAME="computerLanguages" 
       value="Visual Basic">Visual Basic<BR> 
<H3>Enter the spoken languages you know:</H3> 
<TEXTAREA NAME="spokenLanguages" ROWS=5 COLS=20></TEXTAREA> 
<H3>Choose the jr or jx output version:</H3> 
<INPUT TYPE="RADIO" NAME="outputVersion" VALUE="jr" CHECKED>jr 
&nbsp;&nbsp;&nbsp;&nbsp; 
<INPUT TYPE="RADIO" NAME="outputVersion" VALUE="jx">jx 
<P> 
<INPUT TYPE="SUBMIT"> 
</FORM> 
</CENTER> 
</BODY> 
</HTML> 

Listing 12.19. ShowData.java
package moreservlets; 

import java.io.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 
import java.util.*; 

/** Servlet that sets up a variety of data structures, 
 *  then forwards the request to a JSP page that 
 *  uses JSTL to display the values in the 
 *  data structures. 
 */ 
public class ShowData extends HttpServlet {
  public void doGet(HttpServletRequest request, 
                    HttpServletResponse response) 
      throws ServletException, IOException {
    String[] computerLanguages = 
      request.getParameterValues("computerLanguages"); 
    if (computerLanguages == null) {
      computerLanguages = new String[0]; 
    } 
    request.setAttribute("computerLanguages", 
                         computerLanguages); 
    String spokenLanguages = 
      request.getParameter("spokenLanguages"); 
    if (spokenLanguages == null) {
      spokenLanguages = ""; 
    } 
    request.setAttribute("spokenLanguages", 
                         new StringTokenizer(spokenLanguages)); 
    ArrayList favoriteFoods = getFavoriteFoods(); 
    request.setAttribute("favoriteFoods", favoriteFoods); 
    HashMap passwords = getPasswords(); 
    request.setAttribute("passwords", passwords); 
    String bannedUsers = "bill,larry,scott"; 
    request.setAttribute("bannedUsers", bannedUsers); 
    String outputVersion = 
      request.getParameter("outputVersion"); 
    String outputPage = "/forEach/show-data-jx.jsp"; 
    if ("jr".equals(outputVersion)) {
      outputPage = "/forEach/show-data-jr.jsp"; 
    } 
    gotoPage(outputPage, request, response); 
  } 

  private void gotoPage(String address, 
                        HttpServletRequest request, 
                        HttpServletResponse response) 
      throws ServletException, IOException {
    RequestDispatcher dispatcher = 
      getServletContext().getRequestDispatcher(address); 
    dispatcher.forward(request, response); 
  } 
  private ArrayList getFavoriteFoods() {
    ArrayList foods = new ArrayList(); 
    foods.add("Tacos al pastor"); 
    foods.add("Pan-fried dumplings"); 
    foods.add("Bulgogi"); 
    foods.add("Strawberries"); 
    foods.add("Chocolate"); 
    return(foods); 
  } 

  private HashMap getPasswords() {
    HashMap passwords = new HashMap(); 
    passwords.put("bill", "setag"); 
    passwords.put("larry", "nosille"); 
    passwords.put("scott", "ylaencm"); 
    passwords.put("lou", "rentsreg"); 
    passwords.put("greg", "hcneod"); 
    return(passwords); 
  } 
} 

Listing 12.20. show-data-jr.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Data Display: "jr" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<CENTER> 
<TABLE BORDER=5> 
  <TR><TH CLASS="TITLE"> 
      Data Display: "jr" Version 
</TABLE> 
<P> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jr" prefix="jr" %> 
<TABLE BORDER=1> 
<TR> 

<TH CLASS="HEADING">Computer<BR>Languages 
<TH CLASS="HEADING">Spoken<BR>Languages 
<TH CLASS="HEADING">Favorite<BR>Foods 
<TH CLASS="HEADING">Passwords 
<TH CLASS="HEADING">Banned<BR>Users 

<TR> 
<TD> 
<UL> 
<jr:forEach var="lang"
							items='<%= request.getAttribute("computerLanguages") %>'> 
  <LI><%= pageContext.getAttribute("lang") %>
							</jr:forEach> 
</UL> 
<CENTER><SMALL>(From Array)</SMALL></CENTER> 

<TD> 
<UL> 
<jr:forEach var="lang"
							items='<%= request.getAttribute("spokenLanguages") %>'> 
  <LI><%= pageContext.getAttribute("lang") %>
							</jr:forEach> 
</UL> 
<CENTER><SMALL>(From Enumeration)</SMALL></CENTER> 
<TD> 
<UL> 
<jr:forEach var="food"
							items='<%= request.getAttribute("favoriteFoods") %>'> 
  <LI><%= pageContext.getAttribute("food") %>
							</jr:forEach> 
</UL> 
<CENTER><SMALL>(From Collection)</SMALL></CENTER> 
<TD> 
<TABLE BORDER=1> 
  <TR><TH>Username 
      <TH>Password 
<jr:forEach var="user"
							items='<%= request.getAttribute("passwords") %>'>
							<% java.util.Map.Entry user =
							(java.util.Map.Entry)pageContext.getAttribute("user"); %> 
  <TR><TD><%= user.getKey() %> 
      <TD><%= user.getValue() %>
							</jr:forEach> 
</TABLE> 
<CENTER><SMALL>(From Map)</SMALL></CENTER> 

<TD> 
<UL> 
<jr:forEach var="user"
							items='<%= request.getAttribute("bannedUsers") %>'> 
  <LI><%= pageContext.getAttribute("user") %>
							</jr:forEach> 
</UL> 
<CENTER><SMALL>(From String)</SMALL></CENTER> 
</TABLE> 

</CENTER> 
</BODY> 
</HTML> 

Listing 12.21. show-data-jx.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
<TITLE>Data Display: "jx" Version</TITLE> 
<LINK REL=STYLESHEET 
      HREF="../styles.css" 
      TYPE="text/css"> 
</HEAD> 

<BODY> 
<CENTER> 
<TABLE BORDER=5> 
  <TR><TH CLASS="TITLE"> 
      Data Display: "jx" Version 
</TABLE> 
<P> 
<%@ taglib uri="http://java.sun.com/jsptl/ea/jx" prefix="jx" %> 

<TABLE BORDER=1> 

<TR> 
<TH CLASS="HEADING">Computer<BR>Languages 
<TH CLASS="HEADING">Spoken<BR>Languages 
<TH CLASS="HEADING">Favorite<BR>Foods 
<TH CLASS="HEADING">Passwords 
<TH CLASS="HEADING">Banned<BR>Users 

<TR> 
<TD> 
<UL> 
<jx:forEach var="lang" items="$computerLanguages"> 
  <LI><jx:expr value="$lang"/>
							</jx:forEach> 
</UL> 
<CENTER><SMALL>(From Array)</SMALL></CENTER> 

<TD> 
<UL> 
<jx:forEach var="lang" items="$spokenLanguages"> 
  <LI><jx:expr value="$lang"/>
							</jx:forEach> 
</UL> 
<CENTER><SMALL>(From Enumeration)</SMALL></CENTER> 
<TD> 
<UL> 
<jx:forEach var="food" items="$favoriteFoods"> 
  <LI><jx:expr value="$food"/>
							</jx:forEach> 
</UL> 
<CENTER><SMALL>(From Collection)</SMALL></CENTER> 

<TD> 
<TABLE BORDER=1> 
  <TR><TH>Username 
      <TH>Password 
<jx:forEach var="user" items="$passwords"> 
  <TR><TD><jx:expr value="$user.key"/> 
      <TD><jx:expr value="$user.value"/>
							</jx:forEach> 
</TABLE> 
<CENTER><SMALL>(From Map)</SMALL></CENTER> 
<TD> 
<UL> 
<jx:forEach var="user" items="$bannedUsers"> 
  <LI><jx:expr value="$user"/>
							</jx:forEach> 
</UL> 
<CENTER><SMALL>(From String)</SMALL></CENTER> 
</TABLE> 

</CENTER> 
</BODY> 
</HTML> 

Listing 12.22. web.xml (Excerpt for the data-displaying pages)
<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE web-app PUBLIC 
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
    "http://java.sun.com/dtd/web-app_2_3.dtd"> 

<web-app> 
  <!-- Use the SPEL for expressions. --> 
  <context-param> 
    <param-name> 
      javax.servlet.jsptl.ExpressionEvaluatorClass 
    </param-name> 
    <param-value> 
      org.apache.taglibs.jsptl.lang.spel.Evaluator 
    </param-value> 
  </context-param> 

  <!-- ... --> 
  <!-- Give a name to the moreservlets.ShowData servlet 
       so that a custom URL can later be assigned. 
  --> 
  <servlet>
							<servlet-name>ShowData</servlet-name>
							<servlet-class>moreservlets.ShowData</servlet-class>
							</servlet> 

  <!-- ... --> 

  <!-- Register the URL /forEach/ShowData with the 
       ShowData servlet. This lets the HTML form use 
       a simple relative URL to invoke the servlet. 
  --> 
  <servlet-mapping>
							<servlet-name>ShowData</servlet-name>
							<url-pattern>/forEach/ShowData</url-pattern>
							</servlet-mapping> 

  <!-- ... --> 

  <!-- Register jr JSTL TLD file. --> 
  <taglib> 
    <taglib-uri> 
      http://java.sun.com/jsptl/ea/jr 
    </taglib-uri> 
    <taglib-location> 
      /WEB-INF/jsptl-tlds/jsptl-jr.tld 
    </taglib-location> 
  </taglib> 

  <!-- Register jx JSTL TLD file. --> 
  <taglib> 
    <taglib-uri> 
      http://java.sun.com/jsptl/ea/jx 
    </taglib-uri> 
    <taglib-location> 
      /WEB-INF/jsptl-tlds/jsptl-jx.tld 
    </taglib-location> 
  </taglib> 
</web-app> 

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

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