27. JavaServer™ Faces Web Apps: Part 2

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.

Outline

27.1 Introduction

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.3 Ajax

27.4 Adding Ajax Functionality to the Validation App

Summary | Self-Review Exercise | Answers to Self-Review Exercise | Exercises

27.1. Introduction

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.

27.2. Accessing Databases in Web Apps

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.

Image
Image
Image

Fig. 27.1. Sample outputs from 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.

27.2.1. Setting Up the Database

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.

Open NetBeans and Ensure that Java DB and GlassFish Are Running

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.

Creating a Connection Pool

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).

Image

Fig. 27.2. Common Tasks window in the GlassFish server configuration web page.

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.

Image

Fig. 27.3. New JDBC Connection Pool (Step 1 of 2) page.

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)

Image

Fig. 27.4. New JDBC Connection Pool (Step 2 of 2) page.

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.

Creating a Data Source Name

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).

Image

Fig. 27.5. New JDBC Resource page.

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.

Populating the addressbook Database with Sample Data

You’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 Image is displayed next to the database’s URL; otherwise, the icon Image 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.

27.2.2. @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


Fig. 27.6. AddressBean interacts with a database to store and retrieve addresses.

Injecting the 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.

27.2.3. index.xhtml Facelets Page

index.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.

The h:dataTable Element

The 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>


Fig. 27.7. Displays an h:dataTable of the addresses in the address book.

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.

The h:column Elements

Lines 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.

27.2.4. addentry.xhtml Facelets Page

When 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>


Fig. 27.8. Form for adding an entry to an address book.

27.3. Ajax

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.

Traditional Web Applications

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.

Image

Fig. 27.9. Classic web application reloading the page for every user interaction.

Ajax Web 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.

Image

Fig. 27.10. Ajax-enabled web application interacting with the server asynchronously.

27.4. Adding Ajax Functionality to the Validation App

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.

Image
Image
Image
Image

Fig. 27.11. JSP that demonstrates validationofuserinput.

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>


Fig. 27.12. Ajax enabling the Validation app.

Adding id Attributes to Elements

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. 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 Element

The 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 ids—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 ids for the elements that should be updated via partial page updates.

Summary

Section 27.2.1 Setting Up the Database

• 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).

Section 27.2.2 @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.

Section 27.2.3 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".

Section 27.2.4 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.

Section 27.3 Ajax

• 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.

Section 27.4 Adding Ajax Functionality to the Validation App

• 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 ids—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 ids for the elements that should be updated via partial page updates.

Self-Review Exercise

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:dataTables, 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 ids—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 ids for the elements that should be updated via partial page updates.

Answers to Self-Review Exercise

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.

Exercises

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.

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

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