Whatever is in any way beautiful hath its source of beauty in itself, and is complete in itself; praise forms no part of it.
—Marcus Aurelius Antoninus
There is something in a face, An air, and a peculiar grace, Which boldest painters cannot trace.
—William Somerville
Cato said the best way to keep good acts in memory was to refresh them with new.
—Francis Bacon
I never forget a face, but in your case I’ll make an exception.
—Groucho Marx
Objectives
In this chapter you’ll learn:
• To access databases from JSF applications.
• The basic principles and advantages of Ajax technology.
• To use Ajax in a JSF web app.
27.2 Accessing Databases in Web Apps
27.2.1 Setting Up the Database
27.2.2 @ManagedBean
Class AddressBean
27.2.3 index.xhtml
Facelets Page
27.2.4 addentry.xhtml
Facelets Page
27.4 Adding Ajax Functionality to the Validation App
Summary | Self-Review Exercise | Answers to Self-Review Exercise | Exercises
This chapter continues our discussion of JSF web application development with two additional examples. In the first, we present a simple address book app that retrieves data from and inserts data into a Java DB database. The app allows users to view the existing contacts in the address book and to add new contacts. In the second example, we add so-called Ajax capabilities to the Validation
example from Section 26.7. As you’ll learn, Ajax improves application performance and responsiveness. This chapter’s examples, like those in Chapter 26, were developed in NetBeans.
Many web apps access databases to store and retrieve persistent data. In this section, we build an address book web app that uses a Java DB database display contacts from the address book on a web page and to store contacts in the address book. Figure 27.1 shows sample interactions with the AddressBook
app.
If the app’s database already contains addresses, the initial request to the app displays those addresses as shown in Fig. 27.1(a). We populated the database with the sample addresses shown. When the user clicks Add Entry, the addentry.xhtml
page is displayed (Fig. 27.1(b)). When the user clicks Save Address, the form’s fields are validated. If the validations are successful, the address is added to the database and the app returns to the index.xhtml
page to show the updated list of addresses (Fig. 27.1(c)). This example also introduces the h:dataTable
element for displaying data in tabular format.
The next several sections explain how to build the AddressBook
application. First, we set up the database (Section 27.2.1). Next, we present class AddressBean
(Section 27.2.2), which enables the app’s Facelets pages to interact with the database. Finally, we present the index.xthml
(Section 27.2.3) and addentry.xhtml
(Section 27.2.4) Facelets pages.
You’ll now create a data source that enables the app to interact with the database. As part of this process, you’ll create the addressbook
database and populate it with sample data.
Before you can create the data source in NetBeans, the IDE must be open and the Java DB and GlassFish servers must be running. Perform the following steps:
1. Open the NetBeans IDE.
2. On the Services tab, expand the Databases node then right click Java DB. If Java DB is not already running the Start Server option will be enabled. In this case, Select Start server to launch the Java DB server.
3. On the Services tab, expand the Servers node then right click GlassFish Server 3. If GlassFish Server 3 is not already running the Start option will be enabled. In this case, Start server to launch GlassFish.
You may need to wait a few moments for the servers to begin executing.
In web apps that receive many requests, it’s inefficient to create separate database connections for each request. Instead, you should set up a connection pool to allow the server to manage a limited number of database connections and share them among requests. To create a connection pool for this app, perform the following steps:
1. On the Services tab, expand the Servers node, right click GlassFish Server 3 and select View Admin Console. This opens your default web browser and displays a web page for configuring the GlassFish server.
2. In the left column of the page under Common Tasks, expand the Resources node, then expand its JDBC node to show the JDBC Resources and Connection Pools nodes (Fig. 27.2).
3. Click the Connection Pools node to display the list of existing connection pools, then click the New... button above the list to create a new connection pool.
4. In the New JDBC Connection Pool (Step 1 of 2) page (Fig. 27.3), specify AddressBookPool
for the Name, select javax.sql.DataSource
for the Resource Type and select JavaDB for the Database Vendor, then click Next.
5. In the New JDBC Connection Pool (Step 2 of 2) page (Fig. 27.4), scroll to the Additional Properties table and specify the following values (leave the other entries in the table unchanged):
• ConnectionAttributes: ;create=true
(specifies that the database should be created when the connection pool is created)
• DatabaseName: addressbook
(specifies the name of the database to create)
• Password: APP
(specifies the password for the database—the User name is already specified as APP
in the Additional Properties table; you can specify any User name and Password you like)
6. Click Finish to create the connection pool and return to the connection pools list.
7. Click AddressBookPool
in the connection pools list to display the Edit JDBC Connection Pool page, then click Ping in that page to test the database connection and ensure that you set it up correctly.
To connect to the database from the web app, you must configure a data source name that will be used to locate the database. The data source name must be associated with the connection pool that manages the connections to the database. Perform the following steps:
1. In the left column of the GlassFish configuration web page, click the JDBC Resources node to display the list of data source names, then click the New... button to display the New JDBC Resource page (Fig. 27.5).
2. Specify jdbc/addressbook
as the JNDI Name and select AddressBookPool
as the Pool Name. Then click OK. JNDI (Java Naming and Directory Interface) is a technology for locating application components (such as databases) in a distributed application (such as a multitier web application). You can now close the GlassFish configuration web page.
addressbook
Database with Sample DataYou’ll now populate the database with sample data using the AddressBook.sql
SQL script that’s provided with this chapter’s examples. To do so, you must create a connection to the new addressbook
database from NetBeans. Perform the following steps:
1. On the NetBeans Services tab, right click the Databases node and select New Connection....
2. In the New Database Connection dialog, specify localhost
as the Host, 1527
as the Port, addressbook
as the Database, APP
as the User Name and APP
as the Password, then select the Remember password checkbox and click OK.
The preceding steps create a new entry in the Databases node showing the database’s URL (jdbc:derby://localhost:1527/addressbook
). The database server that provides access to this database resides on the local machine and accepts connections on port 1527.
NetBeans must be connected to the database to execute SQL statements. If NetBeans is already connected to the database, the icon is displayed next to the database’s URL; otherwise, the icon is displayed. In this case, right click the icon and select Connect....
To populate the database with sample data, perform the following steps:
1. Click the + next to jdbc:derby://localhost:1527/addressbook
node to expand it, then expand the database’s APP node.
2. Right click the Tables node and select Execute Command... to open a SQL Command editor tab in NetBeans. In a text editor, open the file AddressBook.sql
from this chapter’s examples folder, then copy the SQL statements and paste them into the SQL Command editor in NetBeans. Next, right click in the SQL Command editor and select Run File. This will create the Addresses
table with the sample data in Fig. 27.1(a). [Note: The SQL script attempts to remove the database’s Addresses
table if it already exists. If it doesn’t exist, you’ll receive an error message, but the table will still be created properly.] Expand the Tables node to see the new table. You can view the table’s data by right clicking ADDRESSES and selecting View Data.... Notice that we named the columns with all capital letters. We’ll be using these names in Section 27.2.3.
@ManagedBean
Class AddressBean
[Note: To build this app from scratch, use the techniques you learned in Chapter 26 to create a JSF web application named AddressBook
and add a second Facelets page named addentry.xhtml
to the app.] Class AddressBean
(Fig. 27.6) enables the AddressBook
app to interact with the addressbook
database. The class provides properties that represent the first name, last name, street, city, state and zip code for an entry in the database. These are used by the addentry.xhtml
page when adding a new entry to the database. In addition, this class declares a DataSource
(lines 26–27) for interacting with the database method getAddresses
(lines 102–130) for obtaining the list of addresses from the database and method save
(lines 133–169) for saving a new address into the database. These methods use various JDBC techniques you learned in Chapter 18.
1 // AddressBean.java
2 // Bean for interacting with the AddressBook database
3 package addressbook;
4
5 import java.sql.Connection;
6 import java.sql.PreparedStatement;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import javax.annotation.Resource;
10 import javax.faces.bean.ManagedBean;
11 import javax.sql.DataSource;
12 import javax.sql.rowset.CachedRowSet;
13
14 @ManagedBean( name="addressBean" )
15 public class AddressBean
16 {
17 // instance variables that represent one address
18 private String firstName;
19 private String lastName;
20 private String street;
21 private String city;
22 private String state;
23 private String zipcode;
24
25 // allow the server to inject the DataSource
26 @Resource( name="jdbc/addressbook" )
27 DataSource dataSource;
28
29 // get the first name
30 public String getFirstName()
31 {
32 return firstName;
33 } // end method getFirstName
34
35 // set the first name
36 public void setFirstName( String firstName )
37 {
38 this.firstName = firstName;
39 } // end method setFirstName
40
41 // get the last name
42 public String getLastName()
43 {
44 return lastName;
45 } // end method getLastName
46
47 // set the last name
48 public void setLastName( String lastName )
49 {
50 this.lastName = lastName;
51 } // end method setLastName
52
53 // get the street
54 public String getStreet()
55 {
56 return street;
57 } // end method getStreet
58
59 // set the street
60 public void setStreet( String street )
61 {
62 this.street = street;
63 } // end method setStreet
64
65 // get the city
66 public String getCity()
67 {
68 return city;
69 } // end method getCity
70
71 // set the city
72 public void setCity( String city )
73 {
74 this.city = city;
75 } // end method setCity
76
77 // get the state
78 public String getState()
79 {
80 return state;
81 } // end method getState
82
83 // set the state
84 public void setState( String state )
85 {
86 this.state = state;
87 } // end method setState
88
89 // get the zipcode
90 public String getZipcode()
91 {
92 return zipcode;
93 } // end method getZipcode
94
95 // set the zipcode
96 public void setZipcode( String zipcode )
97 {
98 this.zipcode = zipcode;
99 } // end method setZipcode
100
101 // return a ResultSet of entries
102 public ResultSet getAddresses() throws SQLException
103 {
104 // check whether dataSource was injected by the server
105 if ( dataSource == null )
106 throw new SQLException( "Unable to obtain DataSource" );
107
108 // obtain a connection from the connection pool
109 Connection connection = dataSource.getConnection();
110
111 // check whether connection was successful
112 if ( connection == null )
113 throw new SQLException( "Unable to connect to DataSource" );
114
115 try
116 {
117 // create a PreparedStatement to insert a new address book entry
118 PreparedStatement getAddresses = connection.prepareStatement(
119 "SELECT FIRSTNAME, LASTNAME, STREET, CITY, STATE, ZIP " +
120 "FROM ADDRESSES ORDER BY LASTNAME, FIRSTNAME" );
121
122 CachedRowSet rowSet = new com.sun.rowset.CachedRowSetImpl();
123 rowSet.populate( getAddresses.executeQuery() );
124 return rowSet;
125 } // end try
126 finally
127 {
128 connection.close(); // return this connection to pool
129 } // end finally
130 } // end method getAddresses
131
132 // save a new address book entry
133 public String save() throws SQLException
134 {
135 // check whether dataSource was injected by the server
136 if ( dataSource == null )
137 throw new SQLException( "Unable to obtain DataSource" );
138
139 // obtain a connection from the connection pool
140 Connection connection = dataSource.getConnection();
141
142 // check whether connection was successful
143 if ( connection == null )
144 throw new SQLException( "Unable to connect to DataSource" );
145
146 try
147 {
148 // create a PreparedStatement to insert a new address book entry
149 PreparedStatement addEntry =
150 connection.prepareStatement( "INSERT INTO ADDRESSES " +
151 "(FIRSTNAME,LASTNAME,STREET,CITY,STATE,ZIP)" +
152 "VALUES ( ?, ?, ?, ?, ?, ? )" );
153
154 // specify the PreparedStatement's arguments
155 addEntry.setString( 1, getFirstName() );
156 addEntry.setString( 2, getLastName() );
157 addEntry.setString( 3, getStreet() );
158 addEntry.setString( 4, getCity() );
159 addEntry.setString( 5, getState() );
160 addEntry.setString( 6, getZipcode() );
161
162 addEntry.executeUpdate(); // insert the entry
163 return "index"; // go back to index.xhtml page
164 } // end try
165 finally
166 {
167 connection.close(); // return this connection to pool
168 } // end finally
169 } // end method save
170 } // end class AddressBean
DataSource
into Class AddressBean
A DataSource
(package javax.sql
) enables a web application to obtain a Connection
to a database. Lines 26–27 use annotation @Resource
to inject a DataSource
object into the AddressBean
. The annotation’s name
attribute specifies java/addressbook
—the JNDI name from the Creating a Data Source Name step of Section 27.2.1. The @Resource
annotation enables the server (GlassFish in our case) to hide all the complex details of locating the connection pool that we set up for interacting with the addressbook
database. The server creates a DataSource
for you that’s configured to use that connection pool and assigns the DataSource
object to the annotated variable declared at line 27. You can now trivially obtain a Connection
for interacting with the database.
AddressBean
Method getAddresses
Method getAddresses
(lines 102–130) is called when the index.xhtml
page is requested. The method returns a list of addresses for display in the page (Section 27.2.3). First, we check whether variable dataSource
is null
(lines 105–106), which would indicate that the server was unable to create the DataSource
object. If the DataSource
was created successfully, we use it to obtain a Connection
to the database (line 109). Next, we check whether variable connection
is null
(lines 112–113), which would indicate that we were unable to connect. If the connection was successful, lines 118–124 get the set of addresses from the database and return them.
The PreparedStatement
at lines 118–120 obtains all the addresses. Because database connections are a limited resources, you should use and close them quickly in your web apps. For this reason, we create a CachedRowSet
and populate it with the ResultSet
returned by the PreparedStatement
’s executeQuery
method (lines 122–123). We then return the CachedRowSet
(a disconnected RowSet
) for use in the index.xhtml
page (line 124) and close the connection
object (line 128) in the finally
block.
AddressBean
Method save
Method save
(lines 133–169) stores a new address in the database (Section 27.2.4). This occurs when the user submits the addentry.xhtml
form—assuming the form’s fields validate successfully. As in getAddresses
, we ensure that the DataSource
is not null
, then obtain the Connection
object and ensure that its not null
. Lines 149–152 create a PreparedStatement
for inserting a new record in the database. Lines 155–160 specify the values for each of the parameters in the PreparedStatement
. Line 162 then executes the PreparedStatement
to insert the new record. Line 163 returns the string "index"
, which as you’ll see in Section 27.2.4 causes the app to display the index.xhtml
page again.
index.xhtml
Facelets Pageindex.xhtml
(Fig. 27.7) is the default web page for the AddressBook
app. When this page is requested, it obtains the list of addresses from the AddressBean
and displays them in tabular format using an h:dataTable
element. The user can click the Add Entry button (line 17) to view the addentry.xhtml
page. Recall that the default action for an h:commandButton
is to submit a form. In this case, we specify the button’s action
attribute with the value "addentry"
. The JSF framework assumes this is a page in the app, appends .xhtml
extension to the action
attribute’s value and returns the addentry.xhtml
page to the client browser.
h:dataTable
ElementThe h:dataTable
element (lines 19–46) inserts tabular data into a page. We discuss only the attributes and nested elements that we use here. For more details on this element, its attributes and other JSF tag library elements, visit bit.ly/JSF2TagLibraryReference
.
1 <?xml version='1.0' encoding='UTF-8' ?>
2
3 <!-- index.html -->
4 <!-- Displays an h:dataTable of the addresses in the address book -->
5 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
6 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7 <html xmlns="http://www.w3.org/1999/xhtml"
8 xmlns:h="http://java.sun.com/jsf/html"
9 xmlns:f="http://java.sun.com/jsf/core">
10 <h:head>
11 <title>Address Book</title>
12 <h:outputStylesheet name="style.css" library="css"/>
13 </h:head>
14 <h:body>
15 <h1>Address Book</h1>
16 <h:form>
17 <p><h:commandButton value="Add Entry" action="addentry"/></p>
18 </h:form>
19 <h:dataTable value="#{addressBean.addresses}" var="address"
20 rowClasses="oddRows,evenRows" headerClass="header"
21 styleClass="table" cellpadding="5" cellspacing="0">
22 <h:column>
23 <f:facet name="header">First Name</f:facet>
24 #{address.FIRSTNAME}
25 </h:column>
26 <h:column>
27 <f:facet name="header">Last Name</f:facet>
28 #{address.LASTNAME}
29 </h:column>
30 <h:column>
31 <f:facet name="header">Street</f:facet>
32 #{address.STREET}
33 </h:column>
34 <h:column>
35 <f:facet name="header">City</f:facet>
36 #{address.CITY}
37 </h:column>
38 <h:column>
39 <f:facet name="header">State</f:facet>
40 #{address.STATE}
41 </h:column>
42 <h:column>
43 <f:facet name="header">Zip code</f:facet>
44 #{address.ZIP}
45 </h:column>
46 </h:dataTable>
47 </h:body>
48 </html>
The h:dataTable
element’s value
attribute (line 19) specifies the collection of data you wish to display. In this case, we use AddressBean
’s addresses
property, which calls the getAddresses
method (Fig. 27.6). The collection returned by this method is a CachedRowSet
, which is a type of ResultSet
.
The h:dataTable
iterates over its value
collection and, one at a time, assigns each element to the variable specified by the var
attribute. This variable is used in the h:dataTable
’s nested elements to access each element of the collection—each element in this case represents one row (i.e., address) in the CachedRowSet
.
The rowClasses
attribute (line 20) is a space-separated list of CSS style class names that are used to style the rows in the tabular output. These style classes are defined in the app’s styles.css
file in the css
library (which is inserted into the document at line 12). You can open this file to view the various style class definitions. We specified two style classes—all the odd numbered rows will have the first style (oddRows
) and all the even numbered rows the second style (evenRows
). You can specify as many styles as you like—they’ll be applied in the order you list them one row at a time until all the styles have been applied, then the h:DataTable
will automatically cycle through the styles again for the next set of rows. The columnClasses
attribute works similarly for columns in the table.
The headerClass
attribute (line 20) specifies the column header CSS style. Headers are defined with f:facet
elements nested in h:column
elements (discussed momentarily). The footerClass
attribute works similarly for column footers in the table.
The styleClass
attribute (line 21) specifies the CSS styles for the entire table. The cellpadding
and cellspacing
attributes (line 21) specify the number of pixels around each table cell’s contents and the number of pixels between table cells, respectively.
h:column
ElementsLines 22–45 define the table’s columns with six nested h:column
elements. We focus here on the one at lines 22–25. When the CachedRowSet
is populated in the AddressBean
class, it automatically uses the database’s column names as property names for each row object in the CachedRowSet
. Line 24 inserts into the column the FIRSTNAME
property of the CachedRowSet
’s current row. To display a column header above the column, you define an f:facet
element (line 23) and set its name
attribute to "header"
. Similarly, to display a column footer, use an f:facet
with its name
attribute set to "footer"
. The header is formatted with the CSS style specified in the h:dataTable
’s headerClass
attribute (line 20). The remaining h:column
elements perform similar tasks for the current row’s LASTNAME
, STREET
, CITY
, STATE
and ZIP
properties.
addentry.xhtml
Facelets PageWhen the user clicks Add Entry in the index.xhtml
page, addentry.xhtml
(Fig. 27.8) is displayed. Each h:inputText
in this page has its required
attribute set to "true"
and includes a maxlength
attribute that restricts the user’s input to the maximum length of the corresponding database field. When the user clicks Save (lines 48–49), the input element’s values are validated and (if successful) assigned to the properties of the addressBean
managed object. In addition, the button specifies as its action
the EL expression
#{addressBean.save}
which invokes the addressBean
object’s save
method to store the new address in the database. When you call a method with the action
attribute, if the method returns a value (in this case, it returns the string "index"
), that value is used to request the corresponding page from the app. If the method does not return a value, the current page is re-requested.
1 <?xml version='1.0' encoding='UTF-8' ?>
2
3 <!-- addentry.html -->
4 <!-- Form for adding an entry to an address book -->
5 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
6 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7 <html xmlns="http://www.w3.org/1999/xhtml"
8 xmlns:h="http://java.sun.com/jsf/html">
9 <h:head>
10 <title>Address Book: Add Entry</title>
11 <h:outputStylesheet name="style.css" library="css"/>
12 </h:head>
13 <h:body>
14 <h1>Address Book: Add Entry</h1>
15 <h:form>
16 <h:panelGrid columns="3">
17 <h:outputText value="First name:"/>
18 <h:inputText id="firstNameInputText" required="true"
19 requiredMessage="Please enter first name"
20 value="#{addressBean.firstName}" maxlength="30"/>
21 <h:message for="firstNameInputText" styleClass="error"/>
22 <h:outputText value="Last name:"/>
23 <h:inputText id="lastNameInputText" required="true"
24 requiredMessage="Please enter last name"
25 value="#{addressBean.lastName}" maxlength="30"/>
26 <h:message for="lastNameInputText" styleClass="error"/>
27 <h:outputText value="Street:"/>
28 <h:inputText id="streetInputText" required="true"
29 requiredMessage="Please enter the street address"
30 value="#{addressBean.street}" maxlength="150"/>
31 <h:message for="streetInputText" styleClass="error"/>
32 <h:outputText value="City:"/>
33 <h:inputText id="cityInputText" required="true"
34 requiredMessage="Please enter the city"
35 value="#{addressBean.city}" maxlength="30"/>
36 <h:message for="cityInputText" styleClass="error"/>
37 <h:outputText value="State:"/>
38 <h:inputText id="stateInputText" required="true"
39 requiredMessage="Please enter state"
40 value="#{addressBean.state}" maxlength="2"/>
41 <h:message for="stateInputText" styleClass="error"/>
42 <h:outputText value="Zipcode:"/>
43 <h:inputText id="zipcodeInputText" required="true"
44 requiredMessage="Please enter zipcode"
45 value="#{addressBean.zipcode}" maxlength="5"/>
46 <h:message for="zipcodeInputText" styleClass="error"/>
47 </h:panelGrid>
48 <h:commandButton value="Save Address"
49 action="#{addressBean.save}"/>
50 </h:form>
51 <h:outputLink value="index.xhtml">Return to Addresses</h:outputLink>
52 </h:body>
53 </html>
The term Ajax—short for Asynchronous JavaScript and XML—was coined by Jesse James Garrett of Adaptive Path, Inc., in 2005 to describe a range of technologies for developing highly responsive, dynamic web applications. Ajax applications include Google Maps, Yahoo’s FlickR and many more. Ajax separates the user interaction portion of an application from its server interaction, enabling both to proceed in parallel. This enables Ajax web-based applications to perform at speeds approaching those of desktop applications, reducing or even eliminating the performance advantage that desktop applications have traditionally had over web-based applications. This has huge ramifications for the desktop applications industry—the applications platform of choice is shifting from the desktop to the web. Many people believe that the web—especially in the context of abundant open-source software, inexpensive computers and exploding Internet bandwidth—will create the next major growth phase for Internet companies.
Ajax makes asynchronous calls to the server to exchange small amounts of data with each call. Where normally the entire page would be submitted and reloaded with every user interaction on a web page, Ajax allows only the necessary portions of the page to reload, saving time and resources.
Ajax applications typically make use of client-side scripting technologies such as JavaScript to interact with page elements. They use the browser’s XMLHttpRequest
object to perform the asynchronous exchanges with the web server that make Ajax applications so responsive. This object can be used by most scripting languages to pass XML data from the client to the server and to process XML data sent from the server back to the client.
Using Ajax technologies in web applications can dramatically improve performance, but programming Ajax directly is complex and error prone. It requires page designers to know both scripting and markup languages. As you’ll soon see, JSF 2.0 makes adding Ajax capabilities to your web apps fairly simple.
Figure 27.9 presents the typical interactions between the client and the server in a traditional web application, such as one that uses a user registration form. The user first fills in the form’s fields, then submits the form (Fig. 27.9, Step 1). The browser generates a request to the server, which receives the request and processes it (Step 2). The server generates and sends a response containing the exact page that the browser will render (Step 3), which causes the browser to load the new page (Step 4) and temporarily makes the browser window blank. The client waits for the server to respond and reloads the entire page with the data from the response (Step 4). While such a synchronous request is being processed on the server, the user cannot interact with the client web page. If the user interacts with and submits another form, the process begins again (Steps 5–8).
This model was originally designed for a web of hypertext documents—what some people call the “brochure web.” As the web evolved into a full-scale applications platform, the model shown in Fig. 27.9 yielded “choppy” application performance. Every full-page refresh required users to reestablish their understanding of the full-page contents. Users began to demand a model that would yield the responsiveness of desktop applications.
Ajax applications add a layer between the client and the server to manage communication between the two (Fig. 27.10). When the user interacts with the page, the client creates an XMLHttpRequest
object to manage a request (Step 1). This object sends the request to the server (Step 2) and awaits the response. The requests are asynchronous, so the user can continue interacting with the application on the client side while the server processes the earlier request concurrently. Other user interactions could result in additional requests to the server (Steps 3 and 4). Once the server responds to the original request (Step 5), the XMLHttpRequest
object that issued the request calls a client-side function to process the data returned by the server. This function—known as a callback function—uses partial page updates (Step 6) to display the data in the existing web page without reloading the entire page. At the same time, the server may be responding to the second request (Step 7) and the client side may be starting to do another partial page update (Step 8). The callback function updates only a designated part of the page. Such partial page updates help make web applications more responsive, making them feel more like desktop applications. The web application does not load a new page while the user interacts with it.
The example in this section adds Ajax capabilities to the Validation
app that we presented in Section 26.7. Figure 27.11 shows the sample outputs from the ValidationAjax
version of the app that we’ll build momentarily. Part (a) shows the initial form that’s displayed when this app first executes. Parts (b) and (c) show validation errors that are displayed when the user submits an empty form and invalid data, respectively. Part (d) shows the page after the form is submitted successfully.
As you can see, the app has the same functionality as the version in Section 26.7; however, you’ll notice a couple of changes in how the app works. First, the URL displayed in the web browser always reads localhost:8080/ValidationAjax/
, whereas the URL in the Section 26.7 changes after the form is submitted the first time. Also, in the non-Ajax version of the app, the page refreshes each time you press the Submit button. In the Ajax version, only the parts of the page that need updating actually change.
index.xhtml
The changes required to add Ajax functionality to this app are minimal. All of the changes are in the index.xhtml
file (Fig. 27.12) and are highlighted. The ValidationBean
class is identical to the version in Section 26.7, so we don’t show it here.
1 <?xml version='1.0' encoding='UTF-8' ?>
2
3 <!-- index.xhtml -->
4 <!-- Validating user input -->
5 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
6 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7 <html xmlns="http://www.w3.org/1999/xhtml"
8 xmlns:h="http://java.sun.com/jsf/html"
9 xmlns:f="http://java.sun.com/jsf/core">
10 <h:head>
11 <title>Validating Form Data</title>
12 <h:outputStylesheet name="style.css" library="css"/>
13 </h:head>
14 <h:body>
15 <h:form>
16 <h1>Please fill out the following form:</h1>
17 <p>All fields are required and must contain valid information</p>
18 <h:panelGrid columns="3">
19 <h:outputText value="Name:"/>
20 <h:inputText id="nameInputText" required="true"
21 requiredMessage="Please enter your name"
22 value="#{validationBean.name}"
23 validatorMessage="Name must be fewer than 30 characters">
24 <f:validateLength maximum="30" />
25 </h:inputText>
26 <h:message id="nameMessage" for="nameInputText"
27 styleClass="error"/>
28 <h:outputText value="E-mail:"/>
29 <h:inputText id="emailInputText" required="true"
30 requiredMessage="Please enter a valid e-mail address"
31 value="#{validationBean.email}"
32 validatorMessage="Invalid e-mail address format">
33 <f:validateRegex pattern=
34 "w+([-+.']w+)*@w+([-.]w+)*.w+([-.]w+)*" />
35 </h:inputText>
36 <h:message id="emailMessage" for="emailInputText"
37 styleClass="error"/>
38 <h:outputText value="Phone:"/>
39 <h:inputText id="phoneInputText" required="true"
40 requiredMessage="Please enter a valid phone number"
41 value="#{validationBean.phone}"
42 validatorMessage="Invalid phone number format">
43 <f:validateRegex pattern=
44 "(((d{3}) ?)|(d{3}-))?d{3}-d{4}" />
45 </h:inputText>
46 <h:message id="phoneMessage" for="phoneInputText"
47 styleClass="error"/>
48 </h:panelGrid>
49 <h:commandButton value="Submit">
50 <f:ajax execute="nameInputText emailInputText phoneInputText"
51 render=
52 "nameMessage emailMessage phoneMessage resultOutputText"/>
53 </h:commandButton>
54 <h:outputText id="resultOutputText" escape="false"
55 value="#{validationBean.response}"/>
56 </h:form>
57 </h:body>
58 </html>
id
Attributes to ElementsThe Facelets elements that will be submitted as part of an Ajax request and the Facelets elements that will participate in the partial page updates must have id
attributes. The h:inputText
elements in the original Validation
example already had id
attributes. These elements will be submitted to the server as part of an Ajax request. We’d like the h:Message
elements that show validation errors and the h:outputText
element that displays the result to be updated with partial page updates. For this reason, we’ve added id
attributes to these elements.
f:ajax
ElementThe other key change to this page is at lines 49–53 where the h:commandButton
now contains an f:ajax
element, which intercepts the form submission when the user clicks the button and makes an Ajax request instead. The f:ajax
element’s execute
attribute specifies a space-separated list of element id
s—the values of these elements are submitted as part of the Ajax request. The f:ajax
element’s render
attribute specifies a space-separated list of element id
s for the elements that should be updated via partial page updates.
• A data source enables a web app to interact with a database.
• In web apps that receive many requests, it’s inefficient to create separate database connections for each request. Instead, you should set up a connection pool (p. 27-4) to allow the server to manage a limited number of database connections and share them among requests.
• To connect to the database from a web app, you configure a data source name (p. 27-6) that will be used to locate the database. The data source name is associated with a connection pool that manages the connections to the database.
• JNDI (Java Naming and Directory Interface) is a technology for locating application components (such as databases) in a distributed application (such as a multitier web application).
@ManagedBean
Class AddressBean
• A DataSource
(p. 27-10; package javax.sql
) enables a web application to obtain a Connection
to a database.
• The annotation @Resource
(p. 27-10) can be used to inject a DataSource
object into a managed bean. The annotation’s name
attribute specifies the JNDI name of a data source.
• The @Resource
annotation enables the server to hide all the complex details of locating a connection pool. The server creates a DataSource
for you that’s configured to use a connection pool and assigns the DataSource
object to the annotated variable. You can then trivially obtain a Connection
for interacting with the database.
• Database connections are limited resources, so you should use and close them quickly in your web apps. You can use a CachedRowSet
to store the results of a query for use later.
index.xhtml
Facelets Page• You can use an h:dataTable
element (p. 27-11) to display a collection of objects, such as the rows in a CachedRowSet
, in tabular format.
• If you specify an h:commandButton
’s action
attribute (p. 27-11) with a value that is the name of a web page (without the file name extension), the JSF framework assumes this is a page in the app, appends .xhtml
extension to the action
attribute’s value and returns the page to the client browser.
• The h:dataTable
element’s value
attribute (p. 27-12) specifies the collection of data you wish to display. The h:dataTable
iterates over its value
collection and, one at a time, assigns each element to the variable specified by the var
attribute (p. 27-13). This variable is used in the h:dataTable
’s nested elements to access each element of the collection.
• The h:dataTable rowClasses
attribute (p. 27-13) is a space-separated list of CSS style class names that are used to style the rows in the tabular output. You can specify as many styles as you like—they’ll be applied in the order you list them one row at a time until all the styles have been applied, then the h:DataTable
will automatically cycle through the styles again for the next set of rows. The columnClasses
attribute works similarly for columns in the table.
• The headerClass
attribute (p. 27-13) specifies the column header CSS style. The footerClass
attribute (p. 27-13) works similarly for column footers in the table.
• The styleClass
attribute (p. 27-13) specifies the CSS styles for the entire table. The cellpadding
and cellspacing
attributes (p. 27-13) specify the number of pixels around each table cell’s contents and the number of pixels between table cells, respectively.
• An h:column
element (p. 27-13) defines a column in an h:dataTable
.
• To display a column header above a column, define an f:facet
element (p. 27-13) and set its name
attribute to "header"
. Similarly, to display a column footer, use an f:facet
with its name
attribute set to "footer"
.
addentry.xhtml
Facelets Page• You can call a managed bean’s methods in EL expressions.
• When you call a managed bean method with the action
attribute, if the method returns a value, that value is used to request the corresponding page from the app. If the method does not return a value, the current page is re-requested.
• The term Ajax—short for Asynchronous JavaScript and XML—was coined by Jesse James Garrett of Adaptive Path, Inc., in February 2005 to describe a range of technologies for developing highly responsive, dynamic web applications.
• Ajax separates the user interaction portion of an application from its server interaction, enabling both to proceed asynchronously in parallel. This enables Ajax web-based applications to perform at speeds approaching those of desktop applications.
• Ajax makes asynchronous calls to the server to exchange small amounts of data with each call. Where normally the entire page would be submitted and reloaded with every user interaction on a web page, Ajax reloads only the necessary portions of the page, saving time and resources.
• Ajax applications typically make use of client-side scripting technologies such as JavaScript to interact with page elements. They use the browser’s XMLHttpRequest
object to perform the asynchronous exchanges with the web server that make Ajax applications so responsive.
• In a traditional web application, the user fills in a form’s fields, then submits the form. The browser generates a request to the server, which receives the request and processes it. The server generates and sends a response containing the exact page that the browser will render. The browser loads the new page, temporarily making the browser window blank. The client waits for the server to respond and reloads the entire page with the data from the response. While such a synchronous request is being processed on the server, the user cannot interact with the web page. This model yields “choppy” application performance.
• In an Ajax application, when the user interacts with the page, the client creates an XMLHttpRequest
object to manage a request. This object sends the request to the server and awaits the response. The requests are asynchronous, so the user can interact with the application on the client side while the server processes the earlier request concurrently. Other user interactions could result in additional requests to the server. Once the server responds to the original request, the XMLHttpRequest
object that issued the request calls a client-side function to process the data returned by the server. This callback function uses partial page updates to display the data in the existing web page without reloading the entire page. At the same time, the server may be responding to the second request and the client side may be starting to do another partial page update.
• Partial page updates help make web applications more responsive, making them feel more like desktop applications.
• The Facelets elements that will be submitted as part of an Ajax request and the Facelets elements that will participate in the partial page updates must have id
attributes.
• When you nest an f:ajax
element (p. 27-20) in an h:commandButton
element, the f:ajax
element intercepts the form submission and makes an Ajax request instead.
• The f:ajax
element’s execute
attribute (p. 27-20) specifies a space-separated list of element id
s—the values of these elements are submitted as part of the Ajax request.
• The f:ajax
element’s render
attribute (p. 27-20) specifies a space-separated list of element id
s for the elements that should be updated via partial page updates.
27.1 Fill in the blanks in each of the following statements.
a. Ajax is an acronym for ________.
b. A(n) ________ allows the server to manage a limited number of database connections and share them among requests.
c. is a technology for locating application components (such as databases) in a distributed application.
d. A(n) ________ enables a web application to obtain a Connection
to a database.
e. The annotation ________ can be used to inject a DataSource
object into a managed bean.
f. A(n) ________ element displays a collection of objects in tabular format.
g. An h:commandButton
’s ________ attribute can specify the name of another page in the web app that should be returned to the client.
h. To specify headers or footers for the columns in h:dataTable
s, use ________ elements nested with their name
attributes set to ________ and ________, respectively.
i. ________ separates the user interaction portion of an application from its server interaction, enabling both to proceed asynchronously in parallel.
j. ________ help make web applications more responsive, making them feel more like desktop applications.
k. The f:ajax
element’s ________ attribute specifies a space-separated list of element id
s—the values of these elements are submitted as part of the Ajax request.
l. The f:ajax
element’s ________ attribute specifies a space-separated list of element id
s for the elements that should be updated via partial page updates.
27.1
a. Asynchronous JavaScript and XML.
b. connection pool.
c. JNDI (Java Naming and Directory Interface).
d. DataSource
.
e. @Resource
.
f. h:dataTable
.
g. action
.
h. f:facet
, "header"
, "footer"
.
i. Ajax.
j. partial page updates.
k. execute
.
l. render
.
27.2 (Guestbook
Application) Create a JSF web app that allows users to sign and view a guestbook. Use the Guestbook
database to store guestbook entries. [Note: A SQL script to create the Guestbook
database is provided in the examples directory for this chapter.] The Guestbook
database has a single table, Messages
, which has four columns: Date
, Name
, Email
and Message
. The database already contains a few sample entries. Using the AddressBook
app in Section 27.2 as your guide, create two Facelets pages and a managed bean. The index.xhtml
page should show the Guestbook entries in tabular format and should provide a button to add an entry to the Guestbook. When the user clicks this button, display an addentry.xhtml
page. Provide h:inputText
elements for the user’s name and email address, an h:inputTextarea
for the message and a Sign Guestbook button to submit the form. When the form is submitted, you should store in the Guestbook
database a new entry containing the user’s input and the date of the entry.
27.3 (AddressBook
Application Modification: Ajax) Combine the two Facelets pages of the AddressBook
application (Section 27.2) into a single page. Use Ajax capabilities to submit the new address book entry and to perform a partial page update that rerenders h:dataTable
with the updated list of addresses.
27.4 (AddressBook
Application Modification) Modify your solution to Exercise 27.3 to add a search capability that allows the user to search by last name. When the user presses the Search button, use Ajax to submit the search key and perform a partial page update that displays only the matching addresses in the h:dataTable
.
54.163.221.133