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.
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>
The user interface of a web application is displayed in a web browser.
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
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.
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.
How can a web application know which user is trying to log in when the information of the sample login screen is submitted?
In the following sections, we give an overview of the architecture of a JSF application and show a very simple sample application.
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.
A JavaServer Faces (JSF) page contains HTML and JSF tags.
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.
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 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.
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.
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
@ManagedBean10
@SessionScoped11
public class TimeBean12
{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 time26
*/27
public String getTime()28
{29
Date time = new Date();30
String timeString = timeFormatter.format(time);31
return timeString;32
}33
}
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.
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.
To run a JSF application, you need a server with a JSF container. We suggest that you use the GlassFish application server,
, which has, together with many other features that you can ignore, a JSF container and a convenient administration interface.http://glassfish.dev.java.net
To deploy a JSF application, follow these steps:
Make a separate directory tree for each web application.
Place JSF pages (such as index.xhtml
) into the root directory of the application's directory tree.
Create a WEB-INF
subdirectory in your application directory.
Place all Java classes inside a classes
subdirectory of the WEB-INF
directory. Note that you should place your classes into a package.
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.
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.
Make sure the application server is started. The application server listens to web requests, typically on port 8080.
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.
Point your browser to an URL such as
. Note the http://localhost:8080/time/faces/index.xhtml
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.
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/javaee6
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>
Does a Swing program automatically separate presentation and business logic?
Why does the WAR file need to be deployed to the application server?
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.
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
.
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
publicType
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.
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.
In the value expression of an input tag, the property setter is called when the page is submitted.
What work does the setCity
method of the TimeZoneBean
do?
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}"/>
The outcome string of an action determines the next page that the JSF container sends to the browser.
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"; } }
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 date11
and city.12
*/13
@ManagedBean14
@SessionScoped15
public class TimeZoneBean16
{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 time32
*/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 time41
*/42
public String getCity()43
{44
return city;45
}46
47
/**48
Read-only time property.49
@return the formatted time50
*/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" otherwise64
*/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 zone75
@return the time zone or null if no match is found76
*/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 match91
*/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.
Which page would be displayed if the checkCity
method returned null
?
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).
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"/>
The value attribute of an input component denotes the value that the user supplies.
Table 24.1. Common JSF Components
JSF Tag | Common Attributes | Example | |
---|---|---|---|
Text Field |
|
| |
Password Field |
|
| |
Text Area |
|
value rows cols | |
Radio Button Group |
|
value layout | |
Checkbox |
|
| |
Checkbox Group |
| value layout | |
Menu |
h:selectOneMenu h:selectManyMenu |
| |
Image |
|
| |
Submit Button |
|
value action |
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>
.
How would you supply a set of choices for a credit card expiration year to a h:selectOneMenu
component?
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):
A three-tier application has separate tiers for presentation, business logic, and data storage.
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.
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).
You define data sources in the JSF container and use resource annotations to initialize them.
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>
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.Resource15
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 date21
and city.22
*/23
@ManagedBean24
@SessionScoped25
public class TimeZoneBean26
{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 cities47
*/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 cities56
*/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 cities65
*/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 cities74
*/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 times83
*/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
else100
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" otherwise111
*/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" otherwise125
*/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 zone136
@return the time zone or null if no match is found137
*/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
try145
{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 SQLException160
{161
if (source == null)162
{163
Logger.global.info("No database connection");164
return null;165
}166
Connection conn = source.getConnection();167
try168
{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
else176
return null;177
}178
finally179
{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 match189
*/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
}
Why does the removeCity
method of the TimeZoneBean
return null
or "index"
, depending on the size of the cities
instance variable?
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.
java.text.DateFormat java.util.TimeZone format getAvailableIDs getTimeInstance getTimeZone setTimeZone javax.sql.DataSource java.util.LinkedHashMap getConnection
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?
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.
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.
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.
The data of the POST
request contain a portion username=
the name supplied by the user&password=
the password supplied by the user.
Place an image file, say clock.gif
, into the time
directory, and add a tag <img src="clock.gif"/>
to the index.xhtml
file.
No—it is possible (and sadly common) for programmers to place the business logic into the frame and component classes of the user interface.
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.
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.
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.
Add the tag <h:commandButton value="Help" action="help"/>
to error.xhtml
.
The current page would be redisplayed.
h:selectOneRadio, h:selectOneMenu
, or h:selectOneCheckbox
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}"/>
.
Then the database connection would be kept open for the entire session.
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.
18.188.227.4