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.
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.
<!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> |
<!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> |
<?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> |
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.
<!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> |
<!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> |
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);
}
} |
<?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> |
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.
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.
<!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> |
<!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> |
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); } } |
<?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> |
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.
<!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> |
<!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> |
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.
<!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> |
<!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> |
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.
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.
<!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 <INPUT TYPE="RADIO" NAME="outputVersion" VALUE="jx">jx <P> <INPUT TYPE="SUBMIT"> </FORM> </CENTER> </BODY> </HTML> |
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); } } |
<!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> |
<!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> |
<?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> |
18.119.136.84