Chapter 24. Web Applications

CHAPTER GOALS

  • To understand the web application concept

  • To learn the syntactical elements of the JavaServer Faces web application framework

  • To learn about navigation in web applications

  • To build three-tier web applications

Web applications are a new type of software that has become very important in recent years. Applications for a wide variety of purposes, such as e-mail, banking, shopping, and playing games, run on servers and interact with users through a web browser. Developing web-based user interfaces is more complex and challenging than writing graphical user interfaces. Until recently, only primitive technologies (such as Java servlets) were available for this purpose. Fortunately, more capable frameworks for web programming have emerged that are roughly analogous to the Swing framework for client-side user interface programming. In this chapter, you will learn how to write web applications using the JavaServer Faces (JSF) framework.

The Architecture of a Web Application

A web application is an application whose user interface is displayed in a web browser. The application program resides on the web server. The user fills out form elements and clicks on buttons and links. The user inputs are transmitted over the Internet to the server, and the server program updates the web page that the user sees (see Figure 1).

The browser sends information to the server using the HTTP protocol that was described in Sections 21.2 and 21.5. The server responds by sending a new web page in HTML format.

The web pages that are used in a web application contain forms: groups of elements to collect user input, such as text fields and buttons. For example, here is the HTML code for a simple form that prompts for a user name and password.

<html>
   <head>
      <title>A Simple Form</title>
   </head>
   <body>
      <form action="login.xhtml" method="POST">
         <p>
            User name:
            <input type="text" name="username" />
            Password:
            <input type="password" name="passwd" />
            <input type="submit" name="login" value="Log in"/>
         </p>
      </form>
   </body>
</html>

Note

The user interface of a web application is displayed in a web browser.

The Architecture of a Web Application

Figure 24.1. The Architecture of a Web Application

A Simple Form

Figure 24.2. A Simple Form

Figure 2 shows the form. Note that there are three input elements: a text field, a password field, and a submit button. (The HTML tags are summarized in Appendix F.)

When a submit button is pressed, the form data is submitted to the server. The data is formatted according to the rules of the HTTP protocol, using the POST request type. The form data consists of the names and values of the form elements, formatted in a special way, called "URL encoding". In our example form, the returned data has the following form:

POST /login.xhtml HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 46
blank line
username=jqpublic&passwd=secret&login=Log%20in

Note

When a form is submitted, the names and values of the form elements are sent to the web server.

The web server analyzes the request and sends a new HTML page to the browser. The new page might tell the user that the login was successful and ask the user to specify another action. Alternatively, the new page might tell the user that the login failed.

This simple example illustrates why it is difficult to implement a web application. Imagine what the server program has to do. At any time, it might receive a request with form data. At that point, the server program has to remember which form it has last sent to the client. It then needs to analyze the submitted data, decide what form to show next, and produce the HTML tags for that form.

Note

Upon receiving the form data, the web server sends a new web page to the browser.

There are multiple challenges. As described in Special Topic 24.1 on page 955, the HTTP protocol is stateless—there is no memory of which form was last sent when a new request is received. Generating the HTML tags for a form is tedious. Perhaps most importantly, an application that consists of response strategies for a large number of request types is very hard to comprehend without additional structure.

In order to overcome these challenges, various web application frameworks have been developed. A web application framework hides the low-level details of analyzing HTTP and generating HTML from the application programmer. In this chapter, you will learn about the JavaServer Faces (JSF) framework, the web framework that is a part of the Java Enterprise Edition. You can think of JSF as "Swing for the Web". Both Swing and JSF handle the tedious details of capturing user input and painting text fields and buttons. Swing captures mouse and keyboard events and paints pixels in a frame. JSF handles form-posting events and paints by emitting HTML code. This chapter describes JSF 2.0, an improvement to the original JSF framework, that became available in 2009.

  1. A Simple Form
  2. How can a web application know which user is trying to log in when the information of the sample login screen is submitted?

The Architecture of a JSF Application

In the following sections, we give an overview of the architecture of a JSF application and show a very simple sample application.

JSF Pages

The user interface of a JSF application is described by a set of JSF pages. Each JSF page has the following structure:

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
      <title>Page title</title>
    </h:head>
    <h:body>
      <h:form>
         Page contents
      </h:form>
    </h:body>
</html>

You can think of this as the required "plumbing", similar to the public static void main incantation that is required for every Java program. If you compare this page with the HTML page from the preceding section, you will notice that the main elements are very similar to a regular HTML page, but several elements (head, body, and form) are JSF tags with an h: prefix. Following is a complete example of a JSF page. Figure 3 shows the result of executing the program.

Note

A JavaServer Faces (JSF) page contains HTML and JSF tags.

Executing the time Web Application

Figure 24.3. Executing the time Web Application

ch24/time/index.xhtml

1  <?xml version="1.0" encoding="UTF-8"?>
 2  <html xmlns="http://www.w3.org/1999/xhtml"
 3     xmlns:h="http://java.sun.com/jsf/html">
 4      <h:head>
 5        <title>The time application</title>
 6      </h:head>
 7      <h:body>
 8        <h:form>
 9           <p>
10              The current time is #{timeBean.time}
11           </p>
12        </h:form>
13      </h:body>
14  </html>

The purpose of a JSF page is to generate an HTML page. The basic process is as follows:

  • The HTML tags that are present in the JSF page (such as title and p) are retained. These are the static part of the page: the formatting instructions that do not change.

  • The JSF tags are translated into HTML. This translation is dynamic: it depends on the state of Java objects that are associated with the tags. In our example, the expression #{timeBean.time} has been replaced by dynamically generated text, namely the current time.

Note

The JSF container converts a JSF page to an HTML page, replacing all JSF tags with text and HTML tags.

Figure 4 shows the basic process. The browser requests a JSF page. The page is processed by the JSF container, the server-side software that implements the JSF framework. The JSF container translates all JSF tags into text and HTML tags, yielding a pure HTML page. That page is transmitted to the client browser. The browser displays the page.

The JSF Container Rewrites the Requested Page

Figure 24.4. The JSF Container Rewrites the Requested Page

Managed Beans

The expression #{timeBean.time} is called a value expression. Value expressions invoke method calls on Java objects, which are called managed beans.

These objects are called "managed" because they are controlled by the JSF container. The container creates a managed bean when it is first used in a value expression. The scope of the managed bean determines which clients can access the object and how long the object stays alive.

Note

A managed bean is an object that is controlled by the JSF container.

In this chapter, we only consider managed beans with session scope. A session-scoped object can be accessed by all requests from the same browser. If multiple users are simultaneously accessing a JSF application, each of them is given a separate object. This is a good default for simple web applications.

Here is the code for the TimeBean class. Note the following:

  • You declare a session-scoped managed bean with the annotations @ManagedBean and @SessionScoped.

  • The name of the bean in a value expression is the class name with the first letter changed to lowercase, e.g., timeBean.

  • The value expression timeBean.time calls the getTime method. You will see the reason in the next section.

  • The getTime method uses the DateFormat class to format the current time, producing a string such as 9:00:00 AM.

  • When deploying the application, all class files must be placed inside the WEB-INF/classes directory. Because many application servers also require that classes be contained in a package, we place our classes inside the bigjava package. For that reason, the class is contained in the WEB-INF/classes/bigjava directory.

Note

A bean with session scope is available for multiple requests by the same browser.

ch24/time/WEB-INF/classes/bigjava/TimeBean.java

1  package bigjava;
 2
 3  import java.text.DateFormat;
 4  import java.util.Date;
 5  import java.util.TimeZone;
 6  import javax.faces.bean.ManagedBean;
 7  import javax.faces.bean.SessionScoped;
 8
 9  @ManagedBean
10  @SessionScoped
11  public class TimeBean
12  {
13     private DateFormat timeFormatter;
14
15     /**
16        Initializes the formatter.
17     */
18     public TimeBean()
19     {
20        timeFormatter = DateFormat.getTimeInstance();
21     }
22
23     /**
24        Read-only time property.
25        @return the formatted time
26     */
27     public String getTime()
28     {
29        Date time = new Date();
30        String timeString = timeFormatter.format(time);
31        return timeString;
32     }
33  }

Separation of Presentation and Business Logic

We will look at value expressions and managed beans in more detail in the next section. The key observation is that every JSF application has two parts: presentationand business logic.

The term "presentation" refers to the user interface of the web application: the arrangement of the text, images, buttons, and so on. The business logic is the part of the application that is independent of the visual presentation. In commercial applications, it contains the rules that are used for business decisions: what products to offer, how much to charge, to whom to extend credit, and so on. In our example, we simulated the business logic with a TimeBean object.

Note

The JSF technology enables the separation of presentation and business logic.

JSF pages define the presentation logic. Managed beans define the business logic. Value expressions tie the two together.

The separation of presentation logic and business logic is very important when designing web applications. Some web technologies place the code for the business logic right into the web page. However, this quickly turns into a serious problem. Programmers are rarely skilled in web design (as you can see from the boring web pages in this chapter). Graphic designers don't usually know much about programming and find it very challenging to improve web pages that contain a lot of code. JSF solves this problem. In JSF, the graphic designer only sees the elements that make up the presentation logic. It is easy to take a boring JSF page and make it pretty by adding banners, icons, and so on.

Deploying a JSF Application

To run a JSF application, you need a server with a JSF container. We suggest that you use the GlassFish application server, http://glassfish.dev.java.net, which has, together with many other features that you can ignore, a JSF container and a convenient administration interface.

To deploy a JSF application, follow these steps:

  1. Make a separate directory tree for each web application.

  2. Place JSF pages (such as index.xhtml) into the root directory of the application's directory tree.

  3. Create a WEB-INF subdirectory in your application directory.

  4. Place all Java classes inside a classes subdirectory of the WEB-INF directory. Note that you should place your classes into a package.

  5. Place the file web.xml (which is shown below) inside the WEB-INF subdirectory. Some servers need the web.xml file to configure the JSF container. We also turn on development mode, which gives better error messages.

  6. Zip up all application files into a file with extension .war (Web Archive). This is easily achieved by running the jar command from the command line, after changing to the application directory. For example,

    cd time
    jar cvf time.war .

    The period (.) denotes the current directory. The jar command creates an archive time.war consisting of all files in all subdirectories of the current directory.

  7. Make sure the application server is started. The application server listens to web requests, typically on port 8080.

  8. Deploy the application to the application server. With GlassFish, this can be achieved either through the administrative interface or simply by copying the WAR file into a special deployment directory. By default, this is the subdirectory domains/domain1/autodeploy inside the GlassFish installation directory.

  9. Point your browser to an URL such as http://localhost:8080/time/faces/index.xhtml. Note the faces part in the URL. If you forget this part, the file will not be processed by the JSF container.

Figure 5 shows the directory structure for the application.

The Directory Structure of the time Application

Figure 24.5. The Directory Structure of the time Application

ch24/time/WEB-INF/web.xml

1  <?xml version="1.0" encoding="UTF-8"?>
 2  <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xmlns="http://java.sun.com/xml/ns/javaee"
 4     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 5     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 6         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 7     version="2.5">
 8     <servlet>
 9        <servlet-name>Faces Servlet</servlet-name>
10        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
11     </servlet>
12     <servlet-mapping>
13        <servlet-name>Faces Servlet</servlet-name>
14        <url-pattern>/faces/*</url-pattern>
15     </servlet-mapping>
16     <welcome-file-list>
17        <welcome-file>faces/index.xhtml</welcome-file>
18     </welcome-file-list>
19     <context-param>
20         <param-name>javax.faces.PROJECT_STAGE</param-name>
21         <param-value>Development</param-value>
22     </context-param>
23  </web-app>
  1. The Directory Structure of the time Application
  2. Does a Swing program automatically separate presentation and business logic?

  3. Why does the WAR file need to be deployed to the application server?

JavaBeans Components

A software component is an entity that encapsulates functionality and can be plugged into a software system without programming. A managed bean is an example of a software component. When we added the timeBean object to the web application, we did not write Java code to construct the object or to call its methods.

Note

Properties of a software component can be accessed without having to write Java code.

Some programming languages have explicit support for components, but Java does not. Instead, in Java, you use a programming convention to implement components. A JavaBean is a Java class that follows this convention. A JavaBean exposes properties—values of the component that can be accessed without programming.

Just about any Java class can be a JavaBean—there are only two requirements.

  • A JavaBean must have a constructor with no parameters.

  • A JavaBean must have methods for accessing the component properties that follow the get/set naming convention. For example, to get or set a property named city, the methods must be called getCity and setCity.

Note

A JavaBean is a class that exposes properties through its get and set methods.

In general, if the name of the property is propertyName, and its type is Type, then the associated methods must be of the form

public Type getPropertyName()
public void setPropertyName(Type newValue)

Note that the name of a property starts with a lowercase letter (such as city), but the corresponding methods have an uppercase letter (getCity). The only exception is that property names can be all capitals, such as ID or URL, with corresponding methods getID or setURL.

If a property has only a get method, then it is a read-onlyproperty. If it has only a set method, then it is a write-only property.

A JavaBean can have additional methods, but they are not connected with properties.

Here is a simple example of a bean class that formats the time for a given city, which we will further develop in the next section.

public class TimeZoneBean
{
   // Instance variables
    ...
   // Required constructor with no parameters
   public TimeZoneBean() { ... }

   // city property
   public String getCity() { ... }
   public void setCity(String newValue) { ... }

   // read-only time property
   public String getTime() { ... }

   // Other methods
   ...
}

This bean has two properties: city and time.

You should not make any assumptions about the internal representation of properties in the bean class. The getter and setter methods may simply read or write an instance variable. But they may also do other work. An example is the getTime method from the TimeBean in the preceding section; it formats the current time.

When a property name is used in a value expression that is included in the JSF page, then the get method is involved. For example, when the string

The current time is #{timeBean.time}

is rendered, the JSF container calls the getTime method of the session's TimeBean instance.

Note

In the value expression of an output tag, only the property getter is called.

When a property name is used in an h:inputText tag (that, is the equivalent of an HTML input field or a JTextField), the situation is more complex. Consider this example:

<h:inputText value="#{timeZoneBean.city}"/>

When the JSF page is first displayed, the getCity method is called, and the current value of the city property is displayed. But after the user submits the page, the set-City method is called. It sets the city property to the value that the user typed into the input field.

Note

In the value expression of an input tag, the property setter is called when the page is submitted.

  1. JavaBeans Components
  2. What work does the setCity method of the TimeZoneBean do?

Navigation Between Pages

In most web applications, users will want to move between different pages. For example, a shopping application might have a login page, a page to show products for sale, and a checkout page that shows the shopping cart. In this section, you will learn how to enable users to navigate from one page to another.

Consider our sample timezone program. If the time computation uses the time zone at the server location, it will not be very useful when the user is in another time zone. Therefore, we will prompt for the city in which the user is located. When the user clicks the submit button, we move to the page next.xhtml and display the time in the user's time zone (see Figure 7). However, if no time zone is available for the city, we display the page error.xhtml.

A button yields an outcome, a string that determines the next page. Unless specified otherwise, the next page is the outcome string with the .xhtml extension added. For example, if the outcome string is error, the next page is error.xhtml. (It is possible to specify a different mapping from outcomes to pages, but there is no need to do so for a simple application.)

In many situations, the next page depends on the result of some computation. In our example, we need different outcomes depending on the city that the user entered. To achieve this flexibility, you specify a method expression as the action attribute:

<h:commandButton value="Submit" action="#{timeZoneBean.checkCity}"/>

Note

The outcome string of an action determines the next page that the JSF container sends to the browser.

The timezone Application

Figure 24.7. The timezone Application

A method expression consists of the name of a bean and the name of a method. When the form is submitted, the JSF container calls timeZoneBean.checkCity(). The checkCity method returns the outcome string:

public class TimeZoneBean
{
   ...
   public String checkCity()
   {
      zone = getTimeZone(city);
      if (zone == null) return "error";
      return "next";
   }
}

Note

A method expression specifies a bean and a method that should be invoked on the bean.

If the next page does not depend on a computation, then you set the action attribute of the button to a fixed outcome string, like this:

<h:commandButton value="Back" action="index"/>

If a button has no action attribute, or if the action outcome is null, then the current page is redisplayed.

We can now complete our time zone application. The Java library contains a convenient TimeZone class that knows about time zones across the world. A time zone is identified by a string such as "America/Los_Angeles" or "Asia/Tokyo". The static method getAvailableIDs returns a string array containing all IDs:

String[] ids = TimeZone.getAvailableIDs();

There are several hundred time zone IDs. (We are using time zones in this example because the TimeZone class gives us an interesting data source with lots of data. Later in this chapter, you will see how to access data from a database, but of course that's more complex.)

The static getTimeZone method returns a TimeZone object for a given ID string:

String id = "America/Los_Angeles";
TimeZone zone = TimeZone.getTimeZone(id);

Once you have a TimeZone object, you can use it in conjunction with a DateFormat object to get a time string in that time zone.

DateFormat timeFormatter = DateFormat.getTimeInstance();
timeFormatter.setTimeZone(zone);
Date now = new Date();
// Suppose the server is in New York, and it's noon there
System.out.println(timeFormatter.format(now));
// Prints 9:00:00 AM

Of course, we don't expect the user to know about time zone ID strings, such as "America/Los_Angeles". Instead, we assume that the user will simply enter the city name. The time zone bean will check whether that string, with spaces replaced by underscores, appears at the end of one of the valid time zone IDs.

Here is the code for the bean class.

ch24/timezone/WEB-INF/classes/bigjava/TimeZoneBean.java

1  package bigjava;
 2
 3  import java.text.DateFormat;
 4  import java.util.Date;
 5  import java.util.TimeZone;
 6  import javax.faces.bean.ManagedBean;
 7  import javax.faces.bean.SessionScoped;
 8
 9  /**
10     This bean formats the local time of day for a given date
11     and city.
12  */
13  @ManagedBean
14  @SessionScoped
15  public class TimeZoneBean
16  {
17     private DateFormat timeFormatter;
18     private String city;
19     private TimeZone zone;
20
21     /**
22        Initializes the formatter.
23     */
24     public TimeZoneBean()
25     {
26        timeFormatter = DateFormat.getTimeInstance();
27     }
28
29     /**
30        Setter for city  property.
31        @param aCity the city for which to report the local time
32     */
33     public void setCity(String aCity)
34     {
35        city = aCity;
36     }
37
38     /**
39        Getter for city  property.
40        @return  the city for which to report the local time
41     */
42     public String getCity()
43     {
44         return city;
45     }
46
47     /**
48        Read-only time property.
49        @return the formatted time
50     */
51     public String getTime()
52     {
53          if (zone == null) return "not available";
54          timeFormatter.setTimeZone(zone);
55          Date time = new Date();
56          String timeString = timeFormatter.format(time);
57          return timeString;
58     }
59
60     /**
61          Action for checking a city.
62          @return "next" if time zone information is available for the city,
63          "error" otherwise
64     */
65     public String checkCity()
66     {
67          zone = getTimeZone(city);
68          if (zone == null) return "error";
69          return "next";
70     }
71
72     /**
73          Looks up the time zone for a city.
74          @param aCity the city for which to find the time zone
75          @return the time zone or null if no match is found
76     */
77     private static TimeZone getTimeZone(String aCity)
78     {
79          String[] ids = TimeZone.getAvailableIDs();
80          for (int i = 0; i < ids.length; i++)
81             if (timeZoneIDmatch(ids[i], aCity))
82                    return TimeZone.getTimeZone(ids[i]);
83          return null;
84     }
85
86     /**
87        Checks whether a time zone ID matches a city.
88        @param id the time zone ID (e.g., "America/Los_Angeles")
89        @param aCity the city to match (e.g., "Los Angeles")
90        @return true if the ID and city match
91     */
92     private static boolean timeZoneIDmatch(String id, String aCity)
93     {
94        String idCity = id.substring(id.indexOf('/') + 1);
95        return idCity.replace('_', ' ').equals(aCity);
96     }
97  }

Here is the JSF page for setting the city. The h:inputText tag produces an input field and the h:commandButton tag produces a button. (We discuss its action attribute in the next section.) When the user clicks the button, the browser sends the form values (that is, the contents of the input field) back to the web application. The web application calls the setCity method on the bean because the input field has a #{timeZoneBean.city} value expression.

ch24/timezone/index.xhtml

1  <?xml version="1.0" encoding="UTF-8"?>
 2  <html xmlns="http://www.w3.org/1999/xhtml"
 3     xmlns:h="http://java.sun.com/jsf/html">
 4      <h:head>
 5        <title>The timezone application</title>
 6      </h:head>
 7      <h:body>
 8        <h:form>
 9            <p>
10              Set time zone:
11              <h:inputText value="#{timeZoneBean.city}"/>
12            </p>
13            <p>
14              <h:commandButton value="Submit"
15                 action="#{timeZoneBean.checkCity}"/>
16            </p>
17        </h:form>
18      </h:body>
19  </html>

The next JSF page shows the result, using two value expressions that display the city and time properties. These expressions invoke the getCity and getTime methods of the bean class.

ch24/timezone/next.xhtml

1    <?xml version="1.0" encoding="UTF-8"?>
 2    <html xmlns="http://www.w3.org/1999/xhtml"
 3       xmlns:h="http://java.sun.com/jsf/html">
 4       <h:head>
 5          <title>The timezone application</title>
 6       </h:head>
7    <h:body>
 8       <h:form>
 9           <p>
10             The current time in #{timeZoneBean.city} is #{timeZoneBean.time}
11           </p>
12           <p>
13             <h:commandButton value="Back" action="index"/>
14           </p>
15       </h:form>
16     </h:body>
17  </html>

Figure 8 shows the directory structure of the timezone application.

The Directory Structure of the timezone Application

Figure 24.8. The Directory Structure of the timezone Application

  1. The Directory Structure of the timezone Application
  2. Which page would be displayed if the checkCity method returned null?

JSF Components

In this section, you will see the most useful user interface components that you can place on a JSF form. Table 1 shows a summary. For a comprehensive discussion of all JSF components, see Core JavaServer Faces, 3rd ed., by David Geary and Cay Horstmann (Sun Microsystems Press/Prentice Hall, 2010).

Note

There are JSF components for text input, choices, buttons, and images.

Each component has a value attribute that allows you to connect the component value with a bean property, for example

<h:inputSecret value="#{user.password}"/>

The h:inputTextArea component has attributes to specify the rows and columns, such as

<h:inputTextArea value="#{user.comment}" rows="10" cols="40"/>

Note

The value attribute of an input component denotes the value that the user supplies.

Table 24.1. Common JSF Components

Component

JSF Tag

Common Attributes

Example

Text Field

h:inputText

value

Common JSF Components

Password Field

h:inputSecret

value

Common JSF Components

Text Area

h:inputTextArea

value
rows
cols
Common JSF Components

Radio Button Group

h:selectOneRadio

value
layout
Common JSF Components

Checkbox

h:selectOneCheckbox

value

Common JSF Components

Checkbox Group

h:selectManyCheckbox

value
layout
Common JSF Components

Menu

h:selectOneMenu
h:selectManyMenu

value

Common JSF Components

Image

h:graphicImage

value

Common JSF Components

Submit Button

h:commandButton

value
action
Common JSF Components

The radio button and checkbox groups allow you to specify horizontal or vertical layout:

<h:selectOneRadio value="#{burger.topping}" layout="lineDirection">

In European languages, lineDirection means horizontal and pageDirection means vertical. However, in some languages, lines are written top-to-bottom, and the meanings are reversed.

Button groups and menus are more complex than the other user interface components. They require you to specify two properties:

  • the collection of possible choices

  • the actual choice

The value attribute of the component specifies the actual choice to be displayed. The collection of possible choices is defined by a nested f:selectItems tag, like this:

<h:selectOneRadio value="#{creditCardBean.expirationMonth}"
      layout="pageDirection">
   <f:selectItems value="#{creditCardBean.monthChoices}"/>
</h:selectOneRadio>

Use an f:selectItems tag to specify all choices for a component that allows selection from a list of choices.

When you use the f:selectItems tag, you need to add the namespace declaration

xmlns:f="http://java.sun.com/jsf/core"

to the html tag at the top of your JSF page.

The value of the f:selectItems tag must have a type that can describe a list of choices. There are several types that you can use, but the easiest—and the only one that we will discuss—is a Map. The keys of the map are the labels—the strings that are displayed next to each choice. The corresponding map values are the label values—the values that correspond to the selection. For example, a choice map for months would map January to 1, February to 2, and so on:

public class CreditCardBean
{
   ...
   public Map<String, Integer> getMonthChoices()
   {
      Map<String, Integer> choices = new LinkedHashMap<String, Integer>();
      choices.put("January", 1);
      choices.put("February", 2);
      ...
      return choices;
   }
}

Here, we use a LinkedHashMap because we want to visit entries in the order in which they are inserted. This is more useful than a HashMap, which would visit the labels in random order or a TreeMap, which would visit them in alphabetical order (starting with April!).

The type of the value property of the component enclosing the f:selectItems tag must match the type of the map value. For example, creditCardBean.expirationMonth must be an integer, not a string. If multiple selections are allowed, the type of the value property must be a list or array of matching types. For example, if one could choose multiple months, a selectManyRadio component would have a value property with a type such as int[] or ArrayList<Integer>.

  1. Common JSF Components
  2. How would you supply a set of choices for a credit card expiration year to a h:selectOneMenu component?

A Three-Tier Application

In the final JSF example, you will see a web application with a very common structure. In this example, we will use a database for information storage. We will enhance the time zone example by storing additional cities that are not known to the TimeZone class in a database. Such an application is called a three-tier applicationbecause it consists of three separate layers or tiers (see Figure 9):

  • The presentation tier: the web browser

  • The "business logic" tier: the JSF container, the JSF pages, and the JavaBeans

  • The storage tier: the database

Note

A three-tier application has separate tiers for presentation, business logic, and data storage.

Three-Tier Architecture

Figure 24.9. Three-Tier Architecture

Contrast the three-tier architecture with the more traditional client-server or two-tier architecture that you saw in the database programs of Chapter 22. In that architecture, one of the tiers is the database server, which is accessed by multiple client programs on desktops. Each client program has a presentation layer—usually with a specially programmed graphical user interface—and business logic code. (See Figure 10.) When the business logic changes, a new client program must be distributed over all desktops. In contrast, in a three-tier application, the business logic resides on a server. When the logic changes, the server code is updated, while the presentation tier—the browser—remains unchanged. That is much simpler to manage than updating multiple desktops.

In our example, we will have a single database table, CityZone, with city and time zone names (see Figure 11).

ch24/multizone/sql/CityZone.sql

1    CREATE TABLE CityZone (City VARCHAR(40), Zone VARCHAR(40))
 2    INSERT INTO CityZone VALUES ('San Francisco', 'America/Los_Angeles')
 3    INSERT INTO CityZone VALUES ('Hamburg', 'Europe/Rome')
 4    SELECT * FROM CityZone

If the TimeZoneBean can't find the city among the standard time zone IDs, it makes a database query:

SELECT Zone FROM CityZone WHERE City = the requested city

If there is a matching entry in the database, that time zone is returned.

To query the database, the bean needs a Connection object. In Chapter 22, we used the static getConnection method of the DriverManager class to obtain a database connection. However, JSF containers have a better mechanism for configuring a database in one central location so that multiple web applications can access it.

Two-Tier Client-Server Architecture

Figure 24.10. Two-Tier Client-Server Architecture

The CityZone Table

Figure 24.11. The CityZone Table

The GlassFish application server includes the Derby database. It has a predefined data source with the resource name jdbc/_default. In your bean code, you declare an instance variable of type DataSource and tag it with a @Resource annotation, like this:

@Resource(name="jdbc/_default")
private DataSource source;

You can use the administrative interface of GlassFish to define other data sources.

When the application server loads the web application, it automatically initializes this instance variable. Whenever you need a database connection, call

Connection conn = source.getConnection();
try
{
   Use the connection
}
finally
{
   conn.close();
}

The application server provides an additional service: it pools database connections. When a pooled connection is closed, it is not physically terminated but instead returned to a queue and given out again to another caller of the getConnection method. Pooling avoids the overhead of creating new database connections. In a web application, it would be particularly inefficient to connect to the database with every web request. Connection pooling is completely automatic.

In order to make the application more interesting, we enhanced the TimeZoneBean so that it manages a list of cities. You can add cities to the list and remove a selected city (see Figure 12).

Note

You define data sources in the JSF container and use resource annotations to initialize them.

The multizone Application Shows a List of Cities

Figure 24.12. The multizone Application Shows a List of Cities

The Directory Structure of the multizone Application

Figure 24.13. The Directory Structure of the multizone Application

You will find the code for this web application at the end of this section. Figure 13 shows the directory structure of the application.

You have now seen how to use the JavaServer Faces technology to build web applications. JSF takes care of low-level details so that you don't have to think about HTML forms and the HTTP protocol. Instead, you can focus on the presentation and business logic of your application.

ch24/multizone/index.xhtml

1   <?xml version="1.0" encoding="UTF-8"?>
 2   <html xmlns="http://www.w3.org/1999/xhtml"
 3      xmlns:h="http://java.sun.com/jsf/html">
 4       <h:head>
 5         <title>The multizone application</title>
 6       </h:head>
 7       <h:body>
 8         <h:form>
 9             <p>
10               Enter city:
11               <h:inputText value="#{timeZoneBean.cityToAdd}"/>
12             </p>
13             <p>
14               <h:commandButton value="Submit"
15                     action="#{timeZoneBean.addCity}"/>
16             </p>
17         </h:form>
18       </h:body>
19   </html>

ch24/multizone/next.xhtml

1   <?xml version="1.0" encoding="UTF-8"?>
 2   <html xmlns="http://www.w3.org/1999/xhtml"
 3      xmlns:f="http://java.sun.com/jsf/core"
 4      xmlns:h="http://java.sun.com/jsf/html">
 5       <h:head>
 6         <title>The multizone application</title>
 7       </h:head>
 8       <h:body>
 9         <h:form>
10             <p>
11               <h:selectOneRadio value="#{timeZoneBean.cityToRemove}"
12                  layout="pageDirection">
13                  <f:selectItems value="#{timeZoneBean.citiesAndTimes}"/>
14               </h:selectOneRadio>
15            </p>
16            <p>
17              <h:commandButton value="Remove selected"
18                     action="#{timeZoneBean.removeCity}"/>
19              <h:commandButton value="Add another" action="index"/>
20            </p>
21          </h:form>
22        </h:body>
23    </html>

ch24/multizone/error.xhtml

1  <?xml version="1.0" encoding="UTF-8"?>
 2  <html xmlns="http://www.w3.org/1999/xhtml"
 3     xmlns:h="http://java.sun.com/jsf/html">
 4     <h:head>
 5        <title>The multizone application</title>
 6      </h:head>
 7      <h:body>
 8        <h:form>
 9               <p>
10                 Sorry, no information is available for #{timeZoneBean.cityToAdd}.
11               </p>
12               <p>
13                 <h:commandButton value="Back" action="index"/>
14               </p>
15         </h:form>
16      </h:body>
17  </html>

ch24/multizone/WEB-INF/classes/bigjava/TimeZoneBean.java

1  package bigjava;
 2
 3  import java.sql.Connection;
 4  import java.sql.PreparedStatement;
 5  import java.sql.ResultSet;
 6  import java.sql.SQLException;
 7  import java.text.DateFormat;
 8  import java.util.ArrayList;
 9  import java.util.Date;
10  import java.util.Map;
11  import java.util.TimeZone;
12  import java.util.TreeMap;
13  import java.util.logging.Logger;
14  import javax.annotation.Resource
15  import javax.faces.bean.ManagedBean;
16  import javax.faces.bean.SessionScoped;
17  import javax.sql.DataSource;
18
19  /**
20    This bean formats the local time of day for a given date
21    and city.
22  */
23  @ManagedBean
24  @SessionScoped
25  public class TimeZoneBean
26  {
27    @Resource(name="jdbc/_default")
28    private DataSource source;
29
30   private DateFormat timeFormatter;
31   private ArrayList<String> cities;
32   private String cityToAdd;
33   private String cityToRemove;
34
35   /**
36      Initializes the formatter.
37   */
38   public TimeZoneBean()
39   {
40      timeFormatter = DateFormat.getTimeInstance();
41      cities = new ArrayList<String>();
42   }
43
44   /**
45      Setter for cityToAdd property.
46      @param city the city to add to the list of cities
47   */
48   public void setCityToAdd(String city)
49   {
50         cityToAdd = city;
51   }
52
53   /**
54      Getter for cityToAdd property.
55      @return the city to add to the list of cities
56   */
57   public String getCityToAdd()
58   {
59         return cityToAdd;
60   }
61
62   /**
63      Setter for the cityToRemove property.
64      @param city the city to remove from the list of cities
65   */
66   public void setCityToRemove(String city)
67   {
68         cityToRemove = city;
69   }
70
71   /**
72      Getter for the cityToRemove property.
73      @return the city to remove from the list of cities
74   */
75   public String getCityToRemove()
76   {
77         return cityToRemove;
78   }
79
80   /**
81      Read-only citiesAndTimes property.
82      @return a map containing the cities and formatted times
83   */
84   public Map<String, String> getCitiesAndTimes()
85   {
86      Date time = new Date();
87      Map<String, String> result = new TreeMap<String, String>();
88       for (int i = 0; i < cities.size(); i++)
 89       {
 90         String city = cities.get(i);
 91         String label = city + ": ";
 92         TimeZone zone = getTimeZone(city);
 93         if (zone != null)
 94         {
 95            timeFormatter.setTimeZone(zone);
 96            String timeString = timeFormatter.format(time);
 97            label = label + timeString;
 98         }
 99         else
100            label = label + "unavailable";
101         result.put(label, city);
102       }
103
104       return result;
105    }
106
107    /**
108        Action for adding a city.
109        @return "next" if time zone information is available for the city,
110        "error" otherwise
111    */
112    public String addCity()
113    {
114        TimeZone zone = getTimeZone(cityToAdd);
115        if (zone == null) return "error";
116        cities.add(cityToAdd);
117        cityToRemove = cityToAdd;
118        cityToAdd = "";
119        return "next";
120    }
121
122    /**
123        Action for removing a city.
124        @return null if there are more cities to remove, "index" otherwise
125    */
126    public String removeCity()
127    {
128        cities.remove(cityToRemove);
129        if (cities.size() > 0) return null;
130        else return "index";
131    }
132
133    /**
134         Looks up the time zone for a city.
135        @param city the city for which to find the time zone
136        @return the time zone or null if no match is found
137    */
138    private TimeZone getTimeZone(String city)
139    {
140        String[] ids = TimeZone.getAvailableIDs();
141        for (int i = 0; i < ids.length; i++)
142            if (timeZoneIDmatch(ids[i], city))
143               return TimeZone.getTimeZone(ids[i]);
144        try
145        {
146            String id = getZoneNameFromDB(city);
147             if (id != null)
148             return TimeZone.getTimeZone(id);
149       }
150       catch (Exception ex)
151       {
152          Logger.global.info("Caught in TimeZone.getTimeZone: "
153                   + ex);
154       }
155       return null;
156    }
157
158    private String getZoneNameFromDB(String city)
159              throws SQLException
160    {
161       if (source == null)
162       {
163          Logger.global.info("No database connection");
164          return null;
165       }
166       Connection conn = source.getConnection();
167       try
168       {
169          PreparedStatement stat = conn.prepareStatement(
170               "SELECT Zone FROM CityZone WHERE City=?");
171          stat.setString(1, city);
172          ResultSet result = stat.executeQuery();
173          if (result.next())
174                 return result.getString(1);
175          else
176                 return null;
177          }
178          finally
179          {
180              conn.close();
181          }
182       }
183
184       /**
185           Checks whether a time zone ID matches a city.
186           @param id the time zone ID (e.g., "America/Los_Angeles")
187           @param city the city to match (e.g., "Los Angeles")
188           @return true if the ID and city match
189       */
190       private static boolean timeZoneIDmatch(String id, String city)
191       {
192           String idCity = id.substring(id.indexOf('/') + 1);
193           return idCity.replace('_', ' ').equals(city);
194       }
195     }
  1. The Directory Structure of the multizone Application
  2. Why does the removeCity method of the TimeZoneBean return null or "index", depending on the size of the cities instance variable?

Summary of Learning Objectives

Describe the architecture of a web application.

  • The user interface of a web application is displayed in a web browser.

  • When a form is submitted, the names and values of the form elements are sent to the web server.

  • Upon receiving the form data, the web server sends a new web page to the browser.

Describe the architecture of a JSF application.

  • A JavaServer Faces (JSF) page contains HTML and JSF tags.

  • The JSF container converts a JSF page to an HTML page, replacing all JSF tags with text and HTML tags.

  • A managed bean is an object that is controlled by the JSF container.

  • A bean with session scope is available for multiple requests by the same browser.

  • The JSF technology enables the separation of presentation and business logic.

Explain how properties are defined in managed beans and accessed in value expressions.

  • Properties of a software component can be accessed without having to write Java code.

  • A JavaBean is a class that exposes properties through its get and set methods.

  • In the value expression of an output tag, only the property getter is called.

  • In the value expression of an input tag, the property setter is called when the page is submitted.

Implement navigation between pages.

  • The outcome string of an action determines the next page that the JSF container sends to the browser.

  • A method expression specifies a bean and a method that should be invoked on the bean.

Use common JSF components for designing a user interface.

  • There are JSF components for text input, choices, buttons, and images.

  • The value attribute of an input component denotes the value that the user supplies.

  • Use an f:selectItems tag to specify all choices for a component that allows selection from a list of choices.

Develop applications that use JSF and a database.

  • A three-tier application has separate tiers for presentation, business logic, and data storage.

  • You define data sources in the JSF container and use resource annotations to initialize them.

Classes, Objects, and Methods Introduced in this Chapter

java.text.DateFormat                     java.util.TimeZone
   format                                   getAvailableIDs
   getTimeInstance                          getTimeZone
   setTimeZone                           javax.sql.DataSource
java.util.LinkedHashMap                     getConnection

Media Resources

  • • Lab Exercises

  • Media Resources

Review Exercises

R24.1 What is the difference between a JSF page and a JSF container?

R24.2 What is a bean?

R24.3 What is a bean property?

R24.4 Is a JButton a bean? Why or why not?

R24.5 What is the software engineering purpose of using beans in conjunction with JSF pages?

R24.6 How are variables in the JSF expression language different from variables in Java programs?

R24.7 What are the choices for the scope of a bean? What is the default? When should you choose which scope?

R24.8 How can you implement error checking in a JSF application? Explain, using a login page as an example.

R24.9 What input elements can you place on a JSF form? What are their Swing equivalents?

R24.10 What is the difference between a client-server application and a three-tier application?

Programming Exercises

P24.1 Write a JSF application that reports the values of the following system properties of the web server:

  • The Java version (java.version)

  • The operating system name (os.name)

  • The operating system version (os.version)

Supply a bean that uses the getProperties method of the System class.

P24.2 Write a JSF application that simulates two rolls of a die, producing an output such as "Rolled a 4 and a 6". When the user reloads the page, a new pair of values should be displayed. Supply a bean that encapsulates a Random object.

P24.3 Enhance Exercise P24.2 by producing a web page that shows images of the rolled dice. Find GIF images of dice with numbers 1 through 6 on the front, and generate an HTML page that references the appropriate images. Hint: Use the tag <h:graphic-Image value=imageURL/> and take advantage of the fact that you can embed a value expression into regular text, such as "/image#{expression}.gif".

P24.4 Write a web application that allows a user to specify six lottery numbers. Generate your own combination on the server, and then print out both combinations together with a count of matches.

P24.5 Add error checking to Exercise P24.4. If the lottery numbers are not within the correct range, or if there are duplicates, show an appropriate message and allow the user to fix the error.

P24.6 Personalize the time zone application of Section 24.3. Prompt the user to log in and specify a city to be stored in a profile. The next time the user logs in, the time of their favorite city is displayed automatically. Store users, passwords, and favorite cities in a database. You need a logout button to switch users.

P24.7 Extend Exercise P24.6 so that a user can choose multiple cities and all cities chosen by the user are remembered on the next login.

P24.8 Write a web version of the ExecSQL utility of Chapter 22. Allow users to type arbitrary SQL queries into a text area. Then submit the query to the database and display the result.

P24.9 Produce a web front end for the ATM program in Chapter 12.

P24.10 Produce a web front end for the appointment calendar application of Exercise P12.7.

P24.11 Produce a web front end for the airline reservation program of Exercise P12.8.

Programming Projects

Project 24.1 Write a shopping cart application. A database contains items that can be purchased and their prices, descriptions, and available quantities. If the user wants to check out, ask for the user account. If the user does not yet have an account, create one. The user name and address should be stored with the account in the database. Display an invoice as the last process in the checkout step. When the user has confirmed the purchase, update the quantities in the warehouse.

Project 24.2 Write a web-based grade book application that your instructor might use to manage student grades in this course. Your application should have one account for the instructor, and one account for each student. Instructors can enter and view grades for all students. Students can only see their own grades and their ranking within the course. Implement the features that your instructor uses for determining the course grade (such as dropping the lowest quiz score, counting homework as 30% of the total grade, and so on.) All information should be stored in a database.

Answers to Self-Check Questions

  1. Each protocol has a specific purpose. HTML describes the appearance of a page; it would be useless for sending requests from a browser to a server. HTTP describes a request; it cannot describe the appearance of a page.

  2. The data of the POST request contain a portion username=the name supplied by the user&password=the password supplied by the user.

  3. Place an image file, say clock.gif, into the time directory, and add a tag <img src="clock.gif"/> to the index.xhtml file.

  4. No—it is possible (and sadly common) for programmers to place the business logic into the frame and component classes of the user interface.

  5. The application server knows nothing about the files on your computer. You need to hand it the WAR file with all the application's pages, code, and configuration files so that it can execute the application when it receives a web request.

  6. Technically, yes. It has a constructor with no parameters. However, it has no methods with names that start with get or set, so it exposes no properties.

  7. There is no way of knowing without looking at the source code. Perhaps it simply executes a statement city = newValue, setting an instance variable of the bean class. But the method may also do other work, for example checking whether the city name is valid or storing the name in a database.

  8. Add the tag <h:commandButton value="Help" action="help"/> to error.xhtml.

  9. The current page would be redisplayed.

  10. h:selectOneRadio, h:selectOneMenu, or h:selectOneCheckbox

  11. You would need a bean with a property such as the following:

    public Map<String, Integer> getYearChoices()
    {
       Map<String, Integer> choices = new TreeMap<String, Integer>();
       choices.put("2003", 2003);
       choices.put("2004", 2004);
       ...
       return choices;
    }

    Then supply a tag <f:selectItems value="#{creditCard.yearChoices}"/>.

  12. Then the database connection would be kept open for the entire session.

  13. As long as there are cities, the next.xhtml page is redisplayed. If all cities are removed, it is pointless to display the next.xhtml page, so the application navigates to the index.xhtml page.

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

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