ch27 - ComSciGate

Download Report

Transcript ch27 - ComSciGate

Chapter 27
JavaServer
Pages and Servlets
Chapter Goals
• To implement dynamic web pages with
JavaServer Faces (JSF) technology
• To learn the syntactical elements of
JavaServer Faces
• To learn about navigation in JSF applications
• To build three-tier web applications
A Simple JSF Program
• JSF: Java Server Faces
• To develop a JSF application, you need a web
server that is integrated with a JSF container
• A JSF page contains HTML and JSF tags
• The user interface of a JSF application is
described by a set of JSF pages
A Simple JSF Program
• Each JSF page has the following structure:
<html>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<f:view>
<head>
<title>Page title</title>
</head>
<body>
<h:form>
Page contents
</h:form>
</body>
</f:view>
</html>
A Simple JSF Program
• Previous structure has three parts:
 taglib directives required to locate two JSF libraries
• Tags from the core library have the prefix f: (such
as f:view)
• Tags from the HTML library have the prefix h: (such
as h:form)
 All JSF tags must be contained inside an f:view tag
 The h:form tag encloses all user interface elements
Executing the datetime Web
Application
Figure 1:
Executing the datetime Web Application
File datetime/index.jsp
01: <html>
02:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
03:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
04:
05:
<f:view>
06:
<head>
07:
<title>The datetime application</title>
08:
</head>
09:
<body>
10:
<h:form>
11:
<p>Number of milliseconds since January 1, 1970:
12:
<h:outputText value="#{dateTime.time}"/>
13:
</p>
14:
</h:form>
15:
</body>
16:
</f:view>
17: </html>
The JSF Container Rewrites the
Requested Page
Figure 2:
The JSF Container Rewrites the Requested Page
A Simple JSF Program
• Purpose of a JSF page is to generate an
HTML page
• Basic process:
 HTML tags in the page are retained; they are the
static part of the page
 JSF tags are translated into HTML; translation is
dynamic, it depends on the state of Java objects
• The h: tags generate HTML
• The f: describe structural information that the
h: tags use
 The taglib directives are stripped out
The HTML Code That Is
Generated by a JSF Page
Figure 3:
The HTML Code That Is Generated by a JSF Page
A Simple JSF Program
• The JSF container converts a JSF page to an
HTML page, replacing JSF tags with text and
HTML
• In the example, the h:outputText tag has
the value binding #{dateTime.time}
• Value bindings link JSF pages with Java
objects
Continued
A Simple JSF Program
• The Java objects are defined in a
configuration file
 Named faces-config.xml
 Placed in the WEB-INF subdirectory of the web
application's base directory
File datetime/WEB-INF/facesconfig.xml
01: <?xml version="1.0"?>02:
03: <!DOCTYPE faces-config PUBLIC
04:
"-//Sun Microsystems, Inc.
//DTD JavaServer Faces Config 1.0//EN"
05:
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
06:
07: <faces-config>
08:
<managed-bean>
09:
<managed-bean-name>dateTime</managed-bean-name>
10:
<managed-bean-class>java.util.Date</managed-bean-class>
11:
<managed-bean-scope>request</managed-bean-scope>
12:
</managed-bean>13: </faces-config>
A Simple JSF Program
• This file defines an object dateTime with type
java.util.Date
• A new object is constructed with each
"request"
• Whenever a browser requests the page,
 A new Date object is constructed, and
 It is attached to the dateTime variable
 The Date constructor constructs an object with the
current time
Continued
A Simple JSF Program
• #{dateTime.time} calls getTime of dateTime
• The h:outputText tag converts the result of
that method call to text
Important Design Principle of the
JSF Technology
• JSF enables the separation of presentation
and business logic
• Presentation logic: the user interface of the
web application
• Business logic: the part of the application that
is independent of the visual presentation
• JSF pages define the presentation logic
• Java objects define the business logic
• The value bindings tie the two together
Steps for Deploying a JSF
Application
1. Make a subdirectory with the name of your
web application in the webapps directory of
your Tomcat installation
/usr/local/jakarta-tomcat/webapps/datetime
or
c:\Tomcat\webapps\datetime
2. Place the index.jsp file into that directory
Continued
Steps for Deploying a JSF
Application
3. Create a subdirectory WEB-INF in your
application directory
/usr/local/jakarta-tomcat/webapps/datetime
or
c:\Tomcat\webapps\datetime\WEB-INF
Steps for Deploying a JSF
Application
4. Place faces-config.xml into the WEB-INF
subdirectory
5. Place your Java classes (if any) inside
WEB-INF/classes
6. Place the file web.xml inside the WEB-INF
subdirectory
7. Start the web server
8. Point your browser to
http://localhost:8080/datetime/index.faces
The Directory Structure of the
datetime Application
Figure 4:
The Directory Structure of the datetime Application
The Java Studio Creator Tool
Figure 5:
The Java Studio Creator Tool
File datetime/WEB-INF/web.xml
01: <?xml version="1.0"?>
02:
03: <!DOCTYPE web-app PUBLIC
04:
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
05:
"http://java.sun.com/dtd/web-app_2_3.dtd">
06:
07: <web-app>
08:
<servlet>
09:
<servlet-name>Faces Servlet</servlet-name>
10:
<servlet-class>javax.faces.webapp.FacesServlet
</servlet-class>
11:
<load-on-startup>1</load-on-startup>
12:
</servlet>
13:
14:
<servlet-mapping>
15:
<servlet-name>Faces Servlet</servlet-name>
16:
<url-pattern>*.faces</url-pattern>
17:
</servlet-mapping>
18: </web-app>
Self Check
1. What steps are required to add the image of
a clock to the datetime application? (The
clock doesn't have to show the correct time.)
2. Does a Swing program automatically
separate presentation and business logic?
Answers
1. Place an image file, say clock.gif, into the
datetime directory, and add a tag <img
src="clock.gif"/> to the index.jsp file.
2. No–it is possible (and sadly common) for
programmers to place the business logic
into the frame and component classes of the
user interface.
JavaBeans Components
• Software component:
 Encapsulates functionality
 Can be plugged into a software system without
programming
 For example, the dateTime object
• Unlike some programming languages, Java
does have explicit support for components
Continued
JavaBeans Components
• In Java, 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
JavaBeans Components
• JavaBean requirements:
 Must have a public constructor with no parameters
 Must have methods for accessing the component
properties that follow the get/set naming convention
JavaBeans Components
• For example, to get or set the time of a Date
object, must use getTime and setTime
• For a property with name propertyName and
type Type,
public Type getPropertyName()
public void setPropertyName(Type newValue)
• Exception: the accessor of a boolean
property can use an is prefix
public boolean isShopping()
JavaBeans Components
• The name of a property starts with a
lowercase letter
• The corresponding methods have an
uppercase letter (isShopping)
• Exception: property names can be all capitals
(e.g. ID or URL)
 getID or setURL
Continued
JavaBeans Components
• Read-only property: has only a get method
• Write-only property: has only a set method
• A JavaBean can have additional methods, but
they are not connected with properties
JavaBeans Components
• Many programmers follow the additional
convention that the name of a bean class
should end in Bean
Continued
JavaBeans Components
public class UserBean
{
// Required default constructor
public UserBean() { . . . }
// creditCard property
public String getCreditCard() { . . . }
public void setCreditCard(String newValue) { . . . }
// shopping property public boolean isShopping() { . . . }
public void setShopping(boolean newValue) { . . . }
// Other methods
. . .
// Instance fields
. . .
}
Continued
JavaBeans Components
• This bean has two properties: creditCard
and shopping
• Do not make any assumptions about the
internal representation of properties
 May have an instance field for every property:
private String creditCard;
private boolean shopping;
Continued
JavaBeans Components
• Do not make any assumptions about the
internal representation of properties
 May store the credit card state in a database
• get and set methods would contain database
operations
 May compute the property value:
public boolean isShopping() { return shoppingCart != null; }
JavaBeans Components
• To use a bean in a JSF page, define it in
faces-config.xml
<managed-bean>
<managed-bean-name>user</managed-bean-name>
<managed-bean-class>bigjava.UserBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
• Called a managed bean because the JSF
container manages its lifetime
Continued
JavaBeans Components
• Session scope: the bean is available for
multiple requests by the same browser
• Application scope: the bean stays alive for
the entire web application
 It is shared among different users
JavaBeans Components
• Access the bean properties in value bindings
<h:outputText value="#{user.creditCard}"/>
• Specify the name of the property, not the name
of the get or set methods
 <h:inputText value="#{user.creditCard}"/>
first calls getCreditCard
 When the user submits the form, the setCreditCard
is called to store the edited property value
JavaBeans Components:
An Example
• We want to display the current time, not the
number of milliseconds since January 1, 1970
• Default time computation uses the time zone
at the server location → not very useful
• We will prompt for the city in which the user is
located, and display the time in the user's
time zone
JavaBeans Components:
An Example
• Java library contains a TimeZone class
 A time zone is identified by a string such as
"America/Los_Angeles" or "Asia/Tokyo"
 getAvailableIDs returns a string array containing
all IDs:
String[] ids = TimeZone.getAvailableIDs();
 getTimeZone returns a TimeZone object for
a given ID string:
String id = "America/Los_Angeles";
TimeZone zone = TimeZone.getTimeZone(id);
JavaBeans Components:
An Example
• Use a DateFormat object to get a time string:
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
JavaBeans Components:
An Example
• Interaction with user:
 The user will simply enter the city name
 The time zone bean will replace the spaces in the
name with underscores
 Then, check if that string appears at the end of one of
the valid time zone IDs
The timezone Application
Figure 6:
The timezone Application
File timezone/WEB-INF/classes/
bigjava/TimeZoneBean.java
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
package bigjava;
import java.text.DateFormat;
import java.util.Date;
import java.util.TimeZone;
/**
This bean formats the local time of day for a given date
and city.
*/
public class TimeZoneBean
{
/**
Initializes the formatter.
*/
Continued
File timezone/WEB-INF/classes/
bigjava/TimeZoneBean.java
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
public TimeZoneBean()
{
timeFormatter = DateFormat.getTimeInstance();
}
/**
Setter for city property.
@param aCity the city for which to report the
// local time
*/
public void setCity(String aCity)
{
city = aCity;
zone = getTimeZone(city);
}
Continued
File timezone/WEB-INF/classes/
bigjava/TimeZoneBean.java
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
/**
Getter for city property.
@return the city for which to report the local time
*/
public String getCity()
{
return city;
}
/**
Read-only time property.
@return the formatted time
*/
public String getTime()
{
Continued
File timezone/WEB-INF/classes/
bigjava/TimeZoneBean.java
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
if (zone == null) return "not available";
timeFormatter.setTimeZone(zone);
Date time = new Date();
String timeString = timeFormatter.format(time);
return timeString;
}
/**
Looks up the time zone for a city
@param aCity the city for which to find the time zone
@return the time zone or null if no match is found
*/
private static TimeZone getTimeZone(String city)
{
Continued
File timezone/WEB-INF/classes/
bigjava/TimeZoneBean.java
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
String[] ids = TimeZone.getAvailableIDs();
for (int i = 0; i < ids.length; i++)
if (timeZoneIDmatch(ids[i], city))
return TimeZone.getTimeZone(ids[i]);
return null;
}
/**
Checks whether a time zone ID matches a city
@param id the time zone ID (e.g. "America/Los_Angeles")
@param aCity the city to match (e.g. "Los Angeles")
@return true if the ID and city match
*/
private static boolean timeZoneIDmatch(String id,
// String city)
{
Continued
File timezone/WEB-INF/classes/
bigjava/TimeZoneBean.java
75:
76:
77:
78:
79:
80:
81:
82: }
String idCity = id.substring(id.indexOf('/') + 1);
return idCity.replace('_', ' ').equals(city);
}
private DateFormat timeFormatter;
private String city;
private TimeZone zone;
File timezone/WEB-INF/facesconfig.xml
01: <?xml version="1.0"?>
02:
03: <!DOCTYPE faces-config PUBLIC
04:
"-//Sun Microsystems, Inc.//DTD JavaServer Faces
Config 1.0//EN"
05:
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
06:
07: <faces-config>
08:
<managed-bean>
09:
<managed-bean-name>zone</managed-bean-name>
10:
<managed-bean-class>bigjava.TimeZoneBean<
/managed-bean-class>
11:
<managed-bean-scope>session</managed-bean-scope>
12:
<managed-property>
13:
<property-name>city</property-name>
14:
<value>Los Angeles</value>
15:
</managed-property>
16:
</managed-bean>
17: </faces-config>
File timezone/index.jsp
01: <html>
02:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
03:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
04:
05:
<f:view>
06:
<head>
07:
<title>The timezone application</title>
08:
</head>
09:
<body>
10:
<h:form>
11:
<p>
12:
The current date and time in
13:
<h:outputText value="#{zone.city}"/>
14:
is:
15:
<h:outputText value="#{zone.time}"/> Continued
16:
</p>
17:
<p>
File timezone/index.jsp
18:
Set time zone:
19:
<h:inputText value="#{zone.city}"/>
20:
</p>
21:
<p>
22:
<h:commandButton value="Submit"/>
23:
</p>
24:
</h:form>
25:
</body>
26:
</f:view>
27: </html>
28:
The Directory Structure of the
timezone Application
Figure 7:
The Directory Structure of the timezone Application
Self Check
3. Is the Random class a Java bean?
4. What work does the setCity method of the
TimeZoneBean do besides setting the city
instance field?
5. When you start the timezone application for
the first time, why does the input field
contain the string "Los Angeles"?
Answers
3. Technically, yes. It has a default constructor.
However, it has no methods whose name
start with get or set, so it exposes no
properties.
4. It sets the zone instance field to match the
time zone of the city.
5. When the zone bean was constructed, its
city property was set to "Los Angeles".
When the input field is rendered, its default
value is the current value of the city
property.
Session State and Cookies
Figure 8:
Viewing the Cookies in a Browser
JSF Components
• Each component has a value attribute to
connect the component value with a bean
property
<h:inputSecret value="#{user.password}"/>
• h:inputTextArea has attributes to specify
the rows and columns
<h:inputTextArea value="#{user.comment}"
rows="10" cols="40"/>
Continued
JSF Components
• Radio button and checkbox groups allow you
to specify horizontal or vertical layout:
<h:selectOneRadio value="#{burger.topping}"
layout="lineDirection">
JSF Components:
Button Groups and Menus
• Require two properties:
 the collection of possible choices
 the actual choice
• The value attribute specifies the actual choice
to be displayed
• The collection of possible choices is defined
by a nested f:selectItems tag
<h:selectOneRadio value="#{creditCard.expirationMonth}"
layout="pageDirection">
<f:selectItems value="#{creditCard.monthChoices}"/>
</h:selectOneRadio>
Continued
JSF Components:
Button Groups and Menus
• monthChoices must have a type that can
describe a list of choices
 For example, Map
 The keys of the map are the labels
 The corresponding map values are the label values
Example: Using a Map to Describe
a List of Choices
• To create the list of choices:
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;
}
}
Continued
Example: Using a Map to Describe
a List of Choices
• The type of the value property of the
component must match the type of the map
value
 For example, creditCard.expirationMonth
must be an integer
• If multiple selections are allowed, the type of
the value property must be a list or array of
matching types
Common JSF Components
Self Check
6. Which JSF components can be used to give
a user a choice between "AM/PM" and
"military" time?
7. How would you supply a set of choices for a
credit card expiration year to a
h:selectOneMenu component?
Answers
6. h:selectOneRadio, h:selectOneMenu,
or h:selectOneCheckbox
Answers
7. 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}"/>
Navigation Between Pages
• Consider an enhancement of our timezone
program
• We start with a page that prompts the user to
enter the name of a city
• When the user clicks "Submit" a new page
appears
Continued
Navigation Between Pages
• Next page is either the page with the time
display or an error page if no time zone is
available
• The JSF container needs to determine which
page to show next
Navigating Between Pages
Figure 9:
Navigating Between Pages
Navigation Between Pages
• Each button has an outcome, a string used to
look up the next page
• Generally, next page may depend on the
result of some computation
• We need different outcomes depending on the
city entered
Continued
Navigation Between Pages
• Specify a method binding as the action
attribute:
<h:commandButton value="Submit" action="#{zone.addCity}"/>
• A method binding consists of the name of a
bean and the name of a method
Navigation Between Pages
• When the form is submitted, the JSF engine
calls zone.addCity()
public class TimeZoneBean
{
. . .
public String addCity()
{
if (zone == null) return "unavailable";
// Add the city
. . .
return "available";
}
Continued
Navigation Between Pages
• If next page doesn't depend on a computation,
you set the action attribute of the button to a
fixed outcome string
<h:commandButton value="Back" action="back"/>
Navigation Between Pages
• faces-config.xml contains a navigation rule
that maps outcome strings to pages:
<faces-config>
<navigation-rule>
<navigation-case>
<from-outcome>available</from-outcome>
<to-view-id>/next.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>unavailable</from-outcome>
<to-view-id>/error.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>back</from-outcome>
<to-view-id>/index.jsp</to-view-id>
</navigation-case>
</navigation-rule>
. . .
</faces-config>
Continued
Navigation Between Pages
• Current page is redisplayed when
 The button has no action attribute, or
 The action outcome is not listed in the navigation rules
Self Check
8. What tag would you need to add to
error.jsp so that the user can click on a
button labeled "Help" and see help.jsp?
What other changes do you need to make to
the web application?
9. Which page would be displayed if the
addCity method returned null?
Answers
8. Add the tag <h:commandButton value="Help"
action="help"/> to error.jsp, and add a
navigation rule to faces-config.xml:
<navigation-case>
<from-outcome>help</from-outcome>
<to-view-id>/help.jsp</to-view-id>
</navigation-case>
9. The current page would be redisplayed.
A Three-Tier Application
• A three-tier application has separate tiers for
presentation, business logic, and data
storage
 The presentation tier: the web browser
 The "business logic" tier: the JSF container, the JSF
pages, and the JavaBeans
 The storage tier: the database
Three-Tier Architecture
Figure 10:
Three-Tier Architecture
Two-Tier Client-Server Architecture
Figure 10:
Two-Tier Client-Server Architecture
A Three-Tier Application
• A Three-Tier Application
• If business logic changes
 In a two-tier application, new client program must be
distributed over all desktops
 In a three-tier application, server code is updated,
while presentation tier remains unchanged
• Simpler to manage
A Three-Tier Application
• We will have a single database table,
CityZone, with city and time zone names
• If the TimeZoneBean can't find the city among
the standard time zone IDs, it makes a query:
SELECT Zone FROM CityZone WHERE City = the requested city
• If there is a matching entry in the database,
that time zone is returned
File
multizone/misc/CityZone.sql\
1: CREATE TABLE CityZone (City TEXT, Zone TEXT)
2: INSERT INTO CityZone VALUES ('San Francisco',
'America/Los Angeles')
3: INSERT INTO CityZone VALUES ('Kaoshiung', 'Asia/Taipei')
4: SELECT * FROM CityZone
The cityZone Table
Figure 12:
The cityZone Table
A Three-Tier Application
• To query the database, the bean needs a
Connection object
• With Tomcat, specify the database configuration
in conf/server.xml
 Locate the element
<Host name="localhost" . . . >
 Immediately after, add the configuration information found
on the next slide
 You need to place the JDBC driver file into common/lib
 Restart server after changing the configuration file
A Three-Tier Application
<DefaultContext>
<Resource name="jdbc/mydb" auth="Container"
type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/mydb">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.
BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>driver class</value>
</parameter>
<parameter>
<name>url</name>
<value>database URL</value>
</parameter>
Continued
A Three-Tier Application
<parameter>
<name>username</name>
<value>database user name</value>
</parameter>
<parameter>
<name>password</name>
<value>database user password</value>
</parameter>
</ResourceParams>
</DefaultContext>
A Three-Tier Application
• To obtain a database connection, first look up
the data source that was configured in the
JSF container:
InitialContext ctx = new InitialContext();
DataSource source = (DataSource)
ctx.lookup("java:comp/env/jdbc/mydb");
Connection conn = source.getConnection();
try
{
Use the connection
}
finally
{
conn.close();
}
Continued
A Three-Tier Application
• JSF containers such as Tomcat manage a
pool of database connections
 Pooling avoids the overhead of creating new database
connections
 Pooling is completely automatic
A Three-Tier Application
• Enhanced TimeZoneBean so that it manages
a list of cities
• Can add cities to the list and remove a
selected city
Continued
A Three-Tier Application
Figure 13:
The multizone Application Shows a List of Cities
The Directory Structure of the
multizone Application
Figure 14:
The Directory Structure of the multizone Application
File multizone/index.jsp
01: <html>
02:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
03:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
04:
05:
<f:view>
06:
<head>
07:
<title>The multizone application</title>
08:
</head>
09:
<body>
10:
<h:form>
11:
<p>
12:
Enter city:
13:
<h:inputText value="#{zone.city}"/>
14:
</p>
Continued
15:
<p>
File multizone/index.jsp
16:
17:
18:
19:
20:
21:
<h:commandButton value="Submit"
action="#{zone.addCity}"/>
</p>
</h:form>
</body>
</f:view>
</html>
File multizone/next.jsp
01: <html>
02:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
03:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
04:
05:
<f:view>
06:
<head>
07:
<title>The multizone application</title>
08:
</head>
09:
<body>
10:
<h:form>
11:
<p>
12:
<h:selectOneRadio value="#{zone.cityToRemove}"
13:
layout="pageDirection">
14:
<f:selectItems value=
"#{zone.citiesAndTimes}"/>
15:
</h:selectOneRadio>
Continued
File multizone/next.jsp
16:
17:
18:
19:
20:
21:
22:
23:
24:
</p>
<p>
<h:commandButton value=
"Remove selected" action
="#{zone.removeCity}"/>
<h:commandButton value=
"Add another" action="back"/>
</p>
</h:form>
</body>
</f:view>
</html>
File multizone/error.jsp
01: <html>
02:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
03:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
04:
05:
<f:view>
06:
<head>
07:
<title>The multizone application</title>
08:
</head>
09:
<body>
10:
<h:form>
11:
<p>
12:
Sorry, no information is available for
13:
<h:outputText value="#{zone.city}"/>
14:
</p>
15:
<p>
Continued
File multizone/error.jsp
16:
<h:commandButton value="Back" action="back"/>
17:
</p>
18:
</h:form>
19:
</body>
20:
</f:view>
21: </html>
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
package bigjava;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
java.sql.Connection;
java.sql.PreparedStatement;
java.sql.ResultSet;
java.sql.SQLException;
java.text.DateFormat;
java.util.ArrayList;
java.util.Date;
java.util.Map;
java.util.TimeZone;
java.util.TreeMap;
java.util.logging.Logger;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.sql.DataSource;
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
/**
This bean formats the local time of day for a given date
and city.
*/
public class TimeZoneBean
{
/**
Initializes the formatter.
*/
public TimeZoneBean()
{
timeFormatter = DateFormat.getTimeInstance();
cities = new ArrayList<String>();
}
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
/**
Setter for city property.
@param aCity the city to add to the list of cities
*/
public void setCity(String aCity)
{
city = aCity;
zone = getTimeZone(city);
}
/**
Getter for city property.
@return the city to add to the list of cities
*/
public String getCity()
{
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
return city;
}
/**
Setter for the cityToRemove property
@param aCity the city to remove
*/
public void setCityToRemove(String aCity)
{
cityToRemove = aCity;
}
/**
Getter for the cityToRemove property.
@return the empty string
*/
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
public String getCityToRemove()
{
return cityToRemove;
}
/**
Read-only citiesAndTimes property.
@return a map containing the cities and
// formatted times
*/
public Map<String, String> getCitiesAndTimes()
{
Date time = new Date();
Map<String, String> result
= new TreeMap<String, String>();
for (int i = 0; i < cities.size(); i++)
Continued
{
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
String city = cities.get(i);
String label = city + ": ";
TimeZone zone = getTimeZone(city);
if (zone != null)
{
timeFormatter.setTimeZone(zone);
String timeString = timeFormatter.format(time);
label = label + timeString;
}
else
label = label + "unavailable";
result.put(label, city);
}
return result;
}
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
/**
Action for adding a city.
@return "available" if time zone information is
// available for the city,
"unavailable" otherwise
*/
public String addCity()
{
if (zone == null) return "unavailable";
cities.add(city);
cityToRemove = city;
city = "";
return "available";
}
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
/**
Action for removing a city.
@return null if there are more cities to remove,
// "back" otherwise
*/
public String removeCity()
{
cities.remove(cityToRemove);
if (cities.size() > 0) return null;
else return "back";
}
/**
Looks up the time zone for a city
@param aCity the city for which to find the time zone
@return the time zone or null if no match is found
Continued
*/
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
private static TimeZone getTimeZone(String city)
{
String[] ids = TimeZone.getAvailableIDs();
for (int i = 0; i < ids.length; i++)
if (timeZoneIDmatch(ids[i], city))
return TimeZone.getTimeZone(ids[i]);
try
{
String id = getZoneNameFromDB(city);
if (id != null)
return TimeZone.getTimeZone(id);
}
catch (Exception exception)
{
Logger.global.info("Caught in
TimeZone.getTimeZone: " + exception);
}
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
return null;
}
private static String getZoneNameFromDB(String city)
throws NamingException, SQLException
{
InitialContext ctx = new InitialContext();
DataSource source
= (DataSource)
ctx.lookup("java:comp/env/jdbc/mydb");
Connection conn = source.getConnection();
try
{
PreparedStatement stat = conn.prepareStatement(
"SELECT Zone FROM CityZone WHERE City=?");
stat.setString(1, city);
ResultSet result = stat.executeQuery(); Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
if (result.next())
return result.getString(1);
else
return null;
}
finally
{
conn.close();
}
}
/**
Checks whether a time zone ID matches a city
@param id the time zone ID (e.g. "America/Los_Angeles")
@param aCity the city to match (e.g. "Los Angeles")
@return true if the ID and city match
*/
Continued
File multizone/WEB-INF/classes/
bigjava/TimeZoneBean.java
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187: }
private static boolean timeZoneIDmatch(String id,
String city)
{
String idCity = id.substring(id.indexOf('/') + 1);
return idCity.replace('_', ' ').equals(city);
}
private
private
private
private
private
DateFormat timeFormatter;
ArrayList<String> cities;
String city;
TimeZone zone;
String cityToRemove;
File multizone/WEB-INF/facesconfig.xml
01: <?xml version="1.0"?>
02:
03: <!DOCTYPE faces-config PUBLIC
04:
"-//Sun Microsystems, Inc.//DTD JavaServer
Faces Config 1.0//EN"
05:
"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
06:
07: <faces-config>
08:
<navigation-rule>
09:
<navigation-case>
10:
<from-outcome>available</from-outcome>
11:
<to-view-id>/next.jsp</to-view-id>
12:
</navigation-case>
13:
<navigation-case>
14:
<from-outcome>unavailable</from-outcome>
15:
<to-view-id>/error.jsp</to-view-id>
Continued
16:
</navigation-case>
File multizone/WEB-INF/facesconfig.xml
17:
18:
19:
20:
21:
22:
23:
24:
<navigation-case>
<from-outcome>back</from-outcome>
<to-view-id>/index.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<managed-bean>
<managed-bean-name>zone</managed-bean-name>
<managed-bean-class>bigjava.TimeZoneBean
</managed-bean-class>
25:
<managed-bean-scope>session</managed-bean-scope>
26:
</managed-bean>
27: </faces-config>
Self Check
10. Why don't we just keep a database
connection as an instance field in the
TimeZoneBean?
11. Why does the removeCity method of the
TimeZoneBean return null or "back",
depending on the size of the cities field?
Answers
10. Then the database connection would be
kept open for the entire session.
11. As long as there are cities, the next.jsp
page is redisplayed. If all cities are
removed, it is pointless to display the
next.jsp page, so the application
navigates to the index.jsp page.