Book: JavaServer Pages™, 2nd Edition
Section: Chapter 10.  Sharing Data Between JSP Pages, Requests, and Users



10.1 Passing Control and Data Between Pages

As discussed in Chapter 3, one of the most fundamental features of JSP technology is that it allows for separation of request processing, business logic and presentation, using what's known as the Model-View-Controller (MVC) model. As you may recall, the roles of Model, View, and Controller can be assigned to different types of server-side components. In this part of the book, JSP pages are used for both the Controller and View roles, and the Model role is played by either a bean or a JSP page. This isn't necessarily the best approach, but it lets us focus on JSP features instead of getting into Java programming. If you're a programmer and interested in other role assignments, you may want to take a peek at Chapter 17 and Chapter 18. These chapters describe other alternatives and focus on using a servlet as the Controller.

In this section we look at how to separate the different aspects in a pure JSP application, using a modified version of the User Info example from Chapter 8 as a concrete example. In this application, the business logic piece is trivial. However, it sets the stage for a more advanced application example in the next section and the remaining chapters in this part of the book; all of them use the pattern introduced here.

The different aspects of the User Info example can be categorized like this:

  • Display the form for user input (presentation)

  • Validate the input (request processing and business logic)

  • Display the result of the validation (presentation)

A separate JSP page is used for each aspect in the modified version. The restructured application contains the three JSP pages shown in Figure 10-1.

Figure 10-1. User Info application pages
figs/Jsp2_1001.gif

Here's how it works. The userinfoinput.jsp page displays an input form. The user submits this form to userinputvalidate.jsp to validate the input. This page processes the request using the UserInfoBean and passes control to either the userinfoinput.jsp page (if the input is invalid) or the userinfovalid.jsp page (if the input is valid). If valid, the userinfovalid.jsp page displays a "thank you" message. In this example, the UserInfoBean represents the Model, the userinputvalidate.jsp page the Controller, and userinfoinput.jsp and userinfovalid.jsp represent the Views.

This gives you the flexibility and maintainability discussed in Chapter 3. If the validation rules change, a Java programmer can change the UserInfoBean implementation without touching any other part of the application. If the customer wants a different look, a page author can modify the View JSP pages without touching the request processing or business logic code.

Using different JSP pages as Controller and View means that more than one page is used to process a request. To make this happen, you need to be able to do two things:

  • Pass control from one page to another

  • Pass data from one page to another

10.1.1 Passing Control from One Page to Another

Before digging into the modified example pages, let's go through the basic mechanisms for satisfying the two requirements. As shown in Figure 10-1, the userinfovalidate.jsp page passes control to one of two other pages based on the result of the input validation. JSP supports this through the <jsp:forward> action, described in Table 10-1.

<jsp:forward page="userinfoinput.jsp" />

Table 10-1. Attributes for <jsp:forward>

Attribute name

Java type

Dynamic value accepted

Description

page
String

Yes, but only the scripting kind (see Chapter 15)

Mandatory. A page-relative or context-relative path for the target resource.

The <jsp:forward> action stops processing of one page and starts processing the page specified by the page attribute instead, called the target page. The control never returns to the original page.

The target page has access to all information about the request, including all request parameters. You can also add additional request parameters when you pass control to another page by using one or more nested <jsp:param> action elements (see Table 10-2):

<jsp:forward page="userinfoinput.jsp" >
  <jsp:param name="msg" value="Invalid email address" />
</jsp:forward>

Table 10-2. Attributes for <jsp:param>

Attribute name

Java type

Dynamic value accepted

Description

name
String

No

Mandatory. The parameter name.

value
String

Yes, but only the scripting kind (see Chapter 15)

Mandatory. The parameter value.

Parameters specified with <jsp:param> elements are added to the parameters received with the original request. The target page, therefore, has access to both the original parameters and the new ones, and can access both types in the same way. If a parameter is added to the request using a name of a parameter that already exists, the new value is added first in the list of values for the parameter.

The page attribute is interpreted relative to the location of the current page if it doesn't start with a /. This called a page-relative path. If the source and target page are located in the same directory, just use the name of the target page as the page attribute value, as in the previous example. You can also refer to a file in a different directory using notation such as ../foo/bar.jsp or /foo/bar.jsp. When the page reference starts with a /, it's interpreted relative to the top directory for the application's web page files. This is called a context-relative path.

Let's look at some concrete examples to make this clear. If the application's top directory is C:\Tomcat\webapps\myapp, page references in a JSP page located in C:\Tomcat\webapps\myapp\registration\userinfo are interpreted like this:

page="bar.jsp"

C:\Tomcat\webapps\myapp\registration\userinfo\bar.jsp

page="../foo/bar.jsp"

C:\Tomcat\webapps\myapp\registration\foo\bar.jsp

page="/foo/bar.jsp"

C:\Tomcat\webapps\myapp\foo\bar.jsp

Note that even though Table 10-1 and Table 10-2 show you can use a dynamic value for the <jsp:forward> page attribute and the <jsp:param> value attribute, you can't use an EL expression. The reason for this is discussed in Chapter 15, and alternatives to these two actions that support EL expressions (<ora:forward> and <ora:param>) are introduced later in this book.

10.1.2 Passing Data from One Page to Another

JSP provides different scopes for sharing data objects between pages, requests, and users. The scope defines how long the object is available and whether it's available only to one user or to all application users. The following scopes are defined: page, request, session, and application.

Objects placed in the default scope, the page scope, are available only within that page. That's the scope used in all examples you have seen so far. The request scope is for objects that need to be available to all pages processing the same request. Objects in the session scope are available to all requests made from the same browser, and objects in the application scope are shared by all users of the application (see Figure 10-2). According to the JSP specification, the name used for an object must be unique within all scopes. This means that if you have an object named userInfo in the application scope, for instance, and save another object with the same name in the request scope, the container may remove the first object. Few containers (if any) enforce this rule, but you should ensure you use unique names anyway to avoid portability problems.

Figure 10-2. Lifetime of objects in different scopes
figs/Jsp2_1002.gif

The <jsp:useBean> action has a scope attribute you use to specify the scope for the bean. Here is an example:

<jsp:useBean id="userInfo" scope="request"
  class="com.ora.jsp.beans.userinfo.UserInfoBean" />

The <jsp:useBean> action ensures that the bean already exists in this scope or that a new one is created and placed in the specified scope. It first looks for a bean with the name specified by the id attribute in the specified scope. If it already exists, for instance created by a previously invoked <jsp:useBean> action or by a servlet, it does nothing.[1] If it can't find it, it creates a new instance of the class specified by the class attribute and makes it available with the specified name within the specified scope.

[1] It actually does one thing when the bean already exist: associates the bean with a scripting variable. This is only of interest if you use JSP scripting elements, so I save a discussion about this until Chapter 15.

If you'd like to perform an action only when the bean is created, place the elements in the body of the <jsp:useBean> action:

<jsp:useBean id="userInfo" scope="request"
  class="com.ora.jsp.beans.userinfo.UserInfoBean" >
  <jsp:setProperty name="userInfo" property="*" />
</jsp:useBean>

In this example, the nested <jsp:setProperty> action sets all properties to the values of the corresponding parameters when the bean is created. If the bean already exists, the <jsp:useBean> action body isn't evaluated. and the <jsp:setProperty> action isn't executed.

The scope attribute can also be used with all JSTL actions that expose variables outside their element bodies to designate where the variable should be created, as you will see later in this chapter.

You can access a bean created by the <jsp:useBean> action as a variable in EL expressions. Typically you just specify the variable name no matter which scope it's saved in, for instance:

<c:out value="${userInfo.userName}" />

In this case, the EL looks for the variable in all scopes in the order page, request, session, and application. If it's important to locate a variable in a specific scope, you can use the implicit variables representing the different scopes:

<c:out value="${pageScope.userInfo.userName}" />
<c:out value="${requestScope.userInfo.userName}" />
<c:out value="${sessionScope.userInfo.userName}" />
<c:out value="${applicationScope.userInfo.userName}" />

Each scope variable represents a collection (a java.util.Map) of all variables in that scope, so with expressions like these, the EL looks for the variable only in the specified scope.

10.1.3 All Together Now

At this point, you have seen the two mechanisms needed to let multiple pages process the same request: passing control and passing data. These mechanisms allow you to employ the MVC design, using one page for request processing and business logic, and another for presentation. The <jsp:forward> action can pass control between the pages, and information placed in the request scope is available to all pages processing the same request.

Let's apply this to the User Info example. In Chapter 8, different output was produced depending on whether or not the user input was valid. If the input was invalid, error messages were added to inform the user of the problem. Even when the input was valid, however, the form -- without error messages, of course -- was displayed.

No more of that. When we split the different aspects of the application into separate JSP pages as shown in Figure 10-1, we also change the example so that the form is shown only when something needs to be corrected. When all input is valid, a confirmation page is shown instead.

Example 10-1 shows the top part of the userinfoinput.jsp page.

Example 10-1. Page for displaying entry form (userinfoinput.jsp)
<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
  
<html>
  <head>
    <title>User Info Entry Form</title>
  </head>
  <body bgcolor="white">
    <jsp:useBean id="userInfo" 
      scope="request"
      class="com.ora.jsp.beans.userinfo.UserInfoBean"
    />
  
    <form action="userinfovalidate.jsp" method="post">
    ...

The rest of the page is identical to the one used in Chapter 8. If you compare Example 10-1 with the JSP page used for bean-based validation in Chapter 8, the only differences are that the userInfo bean is placed in the request scope (the scope attribute is set to request), the <jsp:setProperty> action for capturing input is gone, and the form's action attribute specifies the validation page instead of pointing back to the same page.

The validation page, userinfovalidate.jsp, is given in Example 10-2.

Example 10-2. Input validation page (userinfovalidate.jsp)
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
  
<jsp:useBean id="userInfo" 
  scope="request"
  class="com.ora.jsp.beans.userinfo.UserInfoBean">
  <jsp:setProperty name="userInfo" property="*" />
</jsp:useBean>
  
<c:choose>
  <c:when test="${userInfo.valid}">
    <jsp:forward page="userinfovalid.jsp" />
  </c:when>
  <c:otherwise>
    <jsp:forward page="userinfoinput.jsp" />
  </c:otherwise>
</c:choose>

This is the request processing page, which uses the bean to perform the business logic. Note that there's no HTML at all in this page, only a taglib directive declaring the core JSTL library and action elements. This is typical of a request processing page. It doesn't produce a visible response message, it simply takes care of business and passes control to the appropriate presentation page.

This example is relatively simple. First, a new userInfo bean is created in the request scope by the <jsp:useBean> action, and its properties are set from the request parameters values submitted from the form by the nested <jsp:setProperty> action, just as in Chapter 8. A <c:choose> action element with nested <c:when> and <c:otherwise> actions test if the input is valid, using the bean's valid property. The control is passed to the appropriate View page depending of the result, using the <jsp:forward> standard action.

If the input is invalid, the control is passed back to the userinfoinput.jsp page. This time the page continues the processing that originated in the userinfovalidate.jsp page; the <jsp:useBean> action finds the existing userInfo bean in the request scope, and its properties are used to fill out the form fields and add error messages where needed.

If all input is valid, the control is instead passed to the userinfovalid.jsp page shown in Example 10-3 to present the "thank you" message.

Example 10-3. Valid input message page (userinfovalid.jsp)
<html>
  <head>
    <title>User Info Validated</title>
  </head>
  <body bgcolor="white">
    <font color="green" size="+3">
      Thanks for entering valid information!
    </font>
  </body>
</html>

This page tells the user all input was correct. It consists only of template text, so this could have been a regular HTML file. Making it a JSP page allows you to add dynamic content later without changing the referring page, however. The result of submitting valid input is shown in Figure 10-3.

Figure 10-3. The valid input message page
figs/Jsp2_1003.gif

Let's review how placing the bean in the request scope lets you access the same bean in all pages. The user first requests the userinfoinput.jsp page (Example 10-1). A new instance of the userInfo bean is created in the request scope. Because its properties have no values, all form fields are empty at this stage. The user fills out the form and submits it, as a new request, to the userinfovalidate.jsp (Example 10-2) page. The previous bean is then out of scope, so this page creates a new userInfo bean in the request scope and sets all bean properties based on the form field values. If the input is invalid, the <jsp:forward> action passes the control back to the userinfoinput.jsp page. Note that we're still processing the same request that initially created the bean and set all the property values. Since the bean is saved in the request scope, the <jsp:useBean> action finds it and uses it to generate appropriate error messages and fill out the form with any values already entered.

    [http://safari.oreilly.com/059600317X/jserverpages2-CHP-10-SECT-1]


    Copyright © 2002 O'Reilly & Associates, Inc. All rights reserved.
    1005 Gravenstein Highway North
    Sebastopol, CA 95472