Level: Intermediate Richard Hightower (rhightower@arc-mind.com), CTO, ArcMind
01 Mar 2005 In this second article in his four-part JSF for nonbelievers series, Rick Hightower introduces the major phases of the JavaServer Faces (JSF) request processing lifecycle. Using a sample application, he walks you through the six phases of a request process. Along the way, he shows you how to combine JSF with JavaScript technology for immediate event handling and completes your introduction to the JSF component model with a first look at many of the components that ship with JSF.
Editor's notes: Since publication, Sun has open sourced JSF 1.2 under their CDDL license. See Resources for a link to the new project page.
For details on getting started with JSF 1.2, now integrated in JEE 5, see Richard Hightower's tutorial series.
Contrary to popular belief, it's possible to write Java™Server Faces (JSF) applications without knowing every little detail of how the technology works. You can learn a tremendous amount by just giving yourself a project and cobbling through it to the end. On the other hand, understanding certain fundamentals will
make your development efforts much more rewarding -- and a lot less time-consuming.
In this second article in the JSF for nonbelievers series, I'll
walk you through the six phases of the JSF request processing lifecycle.
I'll explain what happens in each phase and how the phases
interconnect, then use an example application to demonstrate the lifecycle in action. Along the way, I'll also show how to work with some of the built-in JSF components briefly touched on in Part 1. I'll also show you how to incorporate Struts Tiles into your JSF development, and how to combine JSF and JavaScript for immediate event handling.
 |
JEE WARTAC blog
Author Rick Hightower takes his discussion of JSF one step further in JEE WARTAC, a blog that focuses on topics related to the tools he uses in his daily development, including JSF, Facelets, Hibernate, Spring, Tomahawk, MyFaces, RDBMS systems, and iBatis.
|
|
As in the previous article, the default build environment for the
example application is Maven. You can download the example source by
clicking the Code icon at the top or bottom of this page. For
simplicity, you'll find the example setup the same as it was in the
previous article. See Resources for further
directions on the build environment setup, including directions for
building and running the example application in Ant, rather than Maven.
The JSF lifecycle: an overview
The six phases of the JSF application lifecycle are as follows (note the event
processing at each phase):
- Restore view
- Apply request values; process events
- Process validations; process events
- Update model values; process events
- Invoke application; process events
- Render response
The six phases show the order in which JSF typically processes a
form GUI. The list shows the phases in their likely order of execution with
event processing at each phase, but the JSF lifecycle is hardly set in
stone. You can change the order of execution by skipping phases or
leaving the lifecycle altogether. For example, if an invalid request
value were copied to a component, the current view would be redisplayed,
and some of the phases might not execute. In this case, you could issue
a FacesContext.responseComplete method invocation to redirect the user to a different page, then use the request
dispatcher (retrieved from the request object in the
FacesContext) to forward to an appropriate Web resource. Alternately, you could call FacesContext.renderResponse
to re-render the original view. (See the sample application below for
details.)
 |
About this series
This four-part series is dedicated to dispelling fear, uncertainty, and doubt (FUD) about JavaServer Faces (JSF) technology, mainly by giving you a chance to get to know it for yourself in a step-by-step, easy-to-follow format. Over the course of four articles, I'll provide a series of examples to introduce you to
the basic architecture, features, and functionality of JSF. Once you're
familiar with the JSF way of doing things, I think you'll find it
hard to go back to Struts Model 2-style development. After all, who
would want to revisit XML configuration Hades after experiencing the JSF
event-driven, GUI component model?
To get the most out of this series, you should be familiar
with Java programming, JavaBeans (namely, event model and properties), JavaServer Pages (JSP), the JSP Standard Tag Library Expression Language, and all basic Web development concepts.
|
|
The point is to let the lifecycle structure your development efforts
without feeling completely tied to it. You can alter the lifecycle when needed without fear of breaking your application. In most cases,
you'll find that the JSF lifecycle is worth adhering to because it's quite logical.
Forms have to be validated before any application logic can
be executed, and field data has to be converted before being validated.
Sticking to the lifecycle frees you up to think about the details of
validation and conversion, rather than the phases of the request process
itself. It's also important to note that other Web frameworks have similar
lifecycles; they just don't tend to be as well advertised.
Focusing your efforts
Some developers using JSF may never write a component or extend the
framework, while others may focus on just those tasks. While the JSF
lifecycle will be the same for almost any project, you'll likely tap
into it at different stages based on your role in the project. If you're
concentrating more on the overall application development, you'll likely
be concerned with the middle phases of the request processing lifecycle:
- Apply requests values
- Process validations
- Update model values
- Invoke application
If you're concentrating on JSF component development, you'll probably
focus on the first and last phases of the lifecycle:
- Restore view
- Render response
In the sections that follow, I'll walk you through every phase of the
JSF request processing lifecycle, including event handling and
validation. Once you have a basic understanding of each phase,
I'll introduce a sample application that shows how they all come
together. Before we get started, take a look at Figure 1, a diagram of
the JSF lifecycle.
Figure 1. The JSF lifecycle
Phase 1: Restore view
In the first phase of the JSF lifecycle -- restore view -- a request comes through the FacesServlet controller. The controller examines the request and extracts the view ID, which is determined by the name of the JSP page.
The JSF framework controller uses the view ID to look up the components for
the current view. If the view doesn't already exist, the JSF controller creates it. If
the view already exists, the JSF controller uses it. The view contains all the GUI
components.
This phase of the lifecycle presents three view instances: new view,
initial view, and postback, with each one being handled differently. In
the case of a new view, JSF builds the view of the Faces page and wires the event handlers and validators to the components. The view is saved in a FacesContext object.
The FacesContext object contains all the state
information JSF needs to manage the GUI component's state for the
current request in the current session. The FacesContext stores the view in its viewRoot property;
viewRoot contains all the JSF components for the current view ID.
In the case of an initial view (the first time a page is
loaded), JSF creates an empty view. The empty view will be populated as
the user causes events to occur. From an initial view, JSF advances
directly to the render response phase.
In the case of a postback (the user returns to a page she has
previously accessed), the view corresponding to the page already exists,
so it needs only to be restored. In this case, JSF uses the existing
view's state information to reconstruct its state. The next phase after
a postback is apply request values.
Phase 2: Apply request values
The purpose of the apply request values phase is for each component to retrieve its current state. The components must first be retrieved or
created from the FacesContext object, followed by their values. Component values are typically retrieved from the request
parameters, although they can also be retrieved from cookies or
headers.
If a component's immediate event handling property is not set
to true, the values are just converted. So if the field is bound to an Integer property, the value is converted to an Integer. If the value conversion fails, an error message is generated and
queued in the FacesContext, where it will be displayed during the render response phase, along with any validation errors.
If a component's immediate event handling property is set to
true, the values are converted to the proper type and validated. The converted value is then stored in the component. If the
value conversion or value validation fails, an error message is generated
and queued in the FacesContext, where it will be displayed during the render response phase, along with any other validation errors.
 |
Immediate event handling
The immediate event handling property of JSF is used to handle events that
normally don't necessitate validating the entire form. For example, say
an employee form has a radio button denoting whether an employee is a
manager. When the employee selects the Manager option, the
application populates a list of perks for managers. Because the radio
button is used only to populate the list and does not require the user to
fill out the entire form, you do not need to validate the form in its
entirety. In this case, you would use immediate event handling.
See Immediate event handling for further details on this subject.
|
|
Phase 3: Process validation
The first event handling of the lifecycle takes place after the
apply request values phase. At this stage, each component will have its
values validated against the application's validation rules. The
validation rules can be pre-defined (shipped with JSF) or
defined by the developer. Values entered by the user are compared
to the validation rules. If an entered value is invalid, an error
message is added to FacesContext, and the component is marked invalid. If a component is marked invalid, JSF advances to the render response phase, which will display the current view with the
validation error messages. If there are no validation errors, JSF
advances to the update model values phase.
Phase 4: Update model values
The fourth phase of the JSF application lifecycle -- update model values -- updates the actual values of the server-side model -- namely, by updating the properties of your backing beans (also known as managed beans). Only
bean properties that are bound to a component's value will be updated.
Notice that this phase happens after validation, so you can be sure that
the values copied to your bean's properties are valid (at least at the
form-field level; they may still be invalid at the business-rule level).
Phase 5: Invoke application
At the fifth phase of the lifecycle -- invoke application -- the JSF controller invokes the application to handle Form submissions. The component values will have been converted, validated, and applied to the model objects, so you can now use them to execute the application's
business logic.
At this phase, you also get to specify the next logical view for a
given sequence or number of possible sequences. You do this by defining
a specific outcome for a successful form submission and returning
that outcome. For example: on successful outcome, move the user to
the next page. For this navigation to work, you will have to create
a mapping to the successful outcome as a navigation rule in the
faces-config.xml file. Once the navigation occurs, you move to the final phase of the lifecycle.
Phase 6: Render response
In the sixth phase of the lifecycle -- render response -- you display the view with all of its components in their current state.
Figure 2 is an object state diagram of the six phases of the JSF lifecycle, including validation and event handling.
Figure 2. The
six-phase progression of the JSF lifecycle
A working example
Now that you have a basic understanding of the phases of the JSF
lifecycle, I'll show you how they all work together in an example Web
application. In addition to demonstrating the basic functions of the JSF
lifecycle, the application utilizes common JSF GUI components like Radio
List, List, Text Field, Label, Panel, and more, so you can experience
first-hand some of the components discussed briefly in Part 1.
The example application also demonstrates a couple of ways to
combine JSF with other Java technologies. It combines JSF and JavaScript
to enable immediate event handling (in cases where validating an entire
form is unwanted), and its layout is managed by Struts Tiles.
Struts Tiles is not necessarily part of JSF, but tiles are commonly
used to lend a consistent look and feel to all the JSF pages in an
application. See Resources to learn more about
Struts Tiles.
Application setup
The example Web application is essentially a simple create,
read, update, and delete (CRUD) listing that manages inventory for an online
CD store. It includes a form that lets the end user enter a new CD into
the system and a list of radio buttons that lets him select a music
category. When the user selects a category, you'll fire off some
JavaScript to post the form immediately back to the server. Combining
JSF with JavaScript to deal with just one component, rather than the
entire page, is called immediate event handling. In this case, it
lets you populate a subcategory list without validating the rest of the
form.
The example application also includes a CD listing that demonstrates
how to work with JSF dataTables. From the listing page, the end user can sort the CD listing by title or by artist.
Classes and methods
Figure 3 shows the classes of the example application. Of the four classes shown, we're concerned only with three: StoreManagerDelegate, CD, and StoreController.
Figure 3. Example application classes
The StoreManagerDelegate class is the application's business delegate. It represents the main interface to the model. The
CD class is a data transfer object (DTO). If this were a real application, the StoreManagerDelegate class would implement all the business rules for adding, deleting, and editing CDs,
and would delegate storing a CD to a persistent store with a data access object (DAO). The StoreManagerDelegate and the CD
comprise the model for this MVC application.
The StoreController class is the main backing bean for this example. The StoreController class is the glue from the GUI world to the model world. It delegates a lot of its behavior to
the StoreManagerDelegate. The StoreController is the controller for this MVC application.
The StoreController class demonstrates how to build a
sortable CRUD listing. It has the following CRUD-related methods:
-
editCD
-
addNew
-
addCD
-
updateCD
The StoreController is also responsible for presenting the model object to the form. In this case, it presents the current CD object to the CD form using its cd property, which is of type CD.
Let's code it
The best way to get started with coding the example application
is to walk through its use cases:
- Add a new CD
- Edit an existing CD
- Sort CDs by title
- Sort CDs by artist
The code for the third and fourth use cases will be nearly identical,
so I'll show you how to sort by title and leave the fourth as
an exercise for you to complete on your own. We'll code the use
cases soon, but first, let's take a look at what the pages will look like for the completed application.
 |
Not quite CRUD
Note that the application is not an actual CRUD listing. It's really just
a CRU listing, as I've left the D for you to implement. But
don't sweat it -- it's quite easy. The steps for the delete operation are similar to
the ones for the edit operation, which I've implemented for you here. Can you
complete the CRUD?
|
|
Figure 4 shows the CD listing page with its sortable columns.
Figure 4. The CD listing page with sortable columns
Figure 5 shows the CD form page with its category component.
Figure 5. The CD form page with no category selected
Figure 6 shows the CD form page with its category and subcategory components.
Figure 6. The CD form page with categories and subcategories selected
Use case 1: Add a new CD
In the first use case for the application, the user adds a new CD by
going to the CD listing page and clicking the Add CD link, which is defined
in the listing.jsp file, as shown in Listing 1.
Listing 1. Add CD button defined in listing.jsp
<h:commandLink action="#{CDManagerBean.addNew}">
<f:verbatim>Add CD</f:verbatim>
</h:commandLink>
|
This link is bound to the CDManagerBean's
addNew method. The addNew method is invoked in the (final) invoke application phase of the JSF lifecycle. The action is bound to this method with the JSF binding expression
#{CDManagerBean.addNew}. CDManagerBean is an alias for the application's store controller.
CDManagerBean is the logical name for the controller. The controller class is a managed bean defined in the faces-config.xml file, as shown in Listing 2.
Listing 2. StoreController class defined in faces-config.xml
<managed-bean>
<description>The "backing file"
bean that backs up the CD application</description>
<managed-bean-name>CDManagerBean</managed-bean-name>
<managed-bean-class>
com.arcmind.jsfquickstart.controller.StoreController
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
|
Preparing the form
The addNew() method prepares the form by creating an empty CD, as shown in Listing 3.
Listing 3. addNew() creates an empty CD form
[StoreController.java]
/**
* Prepare the cdForm to add a new CD.
* This gets executed before we prompt
* the user to add a new CD.
*
* @return success
*/
public String addNew() {
if (subCategoryList == null) {
subCategoryList = new HtmlSelectOneListbox();
}
subCategoryList.setRendered(false);
this.cd = new CD();
return "success";
}
|
The addNew() method blanks out the CD form fields by creating a new CD. The fields of the CD form are bound to the cd property's properties. This method also blanks out the list of subcategories being displayed.
Returning a successful result
Next, the addNew() method is invoked, and control is redirected to the success mapping, which is the cdForm.jsp file. The cdForm.jsp file is defined in the faces-config.xml file, as shown in Listing 4.
Listing 4. cdForm.jsp is the success mapping for addNew()
<navigation-rule>
<from-view-id>/listing.jsp</from-view-id>
...
<navigation-case>
<from-action>#{CDManagerBean.addNew}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/cdForm.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
Listing 4 states that if the user went from the listing to the
addNew (#{CDManagerBean.addNew}) action, and the addNew action returned success, then would go to the cdForm.jsp.
Setting up the cdForm and panelGrid
cdForm.jsp is the form that contains the CD form. It has fields
for ID, Title, Artist, Price, Category, and Subcategory. The fields are
placed in a container called a panelGrid. JSF components, like AWT components, have containers and components. A container is a component that contains other components. This is an example of the composite design pattern. The panelGrid has three columns. Each field is laid out on its own row
with a label and a message to display error messages for the field. The
cdForm and panelGrid are defined in Listing 5.
Listing 5. cdForm and panelGrid defined
<f:view>
<h2>CD Form</h2>
<h:form id="cdForm">
<h:inputHidden id="cdid" value="#{CDManagerBean.cd.id}"/>
<h:panelGrid columns="3" rowClasses="row1, row2">
<h:outputLabel for="title" styleClass="label">
<h:outputText value="Title"/>
</h:outputLabel>
<h:inputText id="title"
value="#{CDManagerBean.cd.title}" required="true"/>
<h:message for="title" styleClass="errorText"/>
<h:outputLabel for="artist" styleClass="label">
<h:outputText value="Artist"/>
</h:outputLabel>
<h:inputText id="artist"
value="#{CDManagerBean.cd.artist}" required="true"/>
<h:message for="artist" styleClass="errorText"/>
<h:outputLabel for="price" styleClass="label">
<h:outputText value="Price"/>
</h:outputLabel>
<h:inputText id="price"
value="#{CDManagerBean.cd.price}" required="true"/>
<h:message for="price" styleClass="errorText"/>
<h:outputLabel for="category" styleClass="label">
<h:outputText value="Category"/>
</h:outputLabel>
<h:selectOneRadio id="category"
value="#{CDManagerBean.cd.category}" immediate="true"
onclick="submit()"
valueChangeListener="#{CDManagerBean.categorySelected}">
<f:selectItems value="#{CDManagerBean.categories}"/>
</h:selectOneRadio>
<h:message for="category" styleClass="errorText"/>
<h:outputLabel for="subcategory" styleClass="label">
<h:outputText value="Subcategory"/>
</h:outputLabel>
<h:selectOneListbox id="subcategory"
value="#{CDManagerBean.cd.subCategory}"
binding="#{CDManagerBean.subCategoryList}">
<f:selectItems value="#{CDManagerBean.subCategories}"/>
</h:selectOneListbox>
<h:message for="subcategory" styleClass="errorText"/>
</h:panelGrid>
<br />
<h:commandButton id="submitAdd"
action="#{CDManagerBean.addCD}" value="Add CD"
rendered="#{not CDManagerBean.editMode}"/>
<h:commandButton id="submitUpdate"
action="#{CDManagerBean.updateCD}" value="Update CD"
rendered="#{CDManagerBean.editMode}"/>
</h:form>
</f:view>
|
Notes about the code
Each input field binds the field to a property of the controller's cd property. For example, the input text field for the title is bound to the cd property with the following JSF binding expression: value="#{CDManagerBean.cd.title}".
You likely noticed that there is hardly any HTML in Listing 5. This
is because the panelGrid generates most of the HMTL. Note that the real look and feel is determined by the style sheets associated with the panelGrid. The attribute rowClasses="row1, row2" sets the CSS classes for alternating rows. Row1 is white, and Row2 is gray. You can also specify the CSS classes for columns and much more. The JSF panelGrid component makes it convenient to quickly lay out a form. If you want to do something that the panelGrid does not provide, you don't have to use it: You can lay out your components using HTML. However, if you find that you use custom HTML on many pages, you may want to consider writing your own custom component. The idea is to make your reusable HTML as DRY as possible (DRY stands for don't repeat yourself, a term coined by Dave Thomas, the Pragmatic Programmer).
Something else to note about Listing 5 is that the controller
presents an editMode property, which is used by the
cdForm.jsp to selectively display the
submitAdd button or thesubmitUpdate button; the submitAdd button is displayed when the form is not in edit mode. The submitUpdate button is displayed when the form is in edit mode. This makes it easy to use the same JSP for edit and add mode. (By default, the form is not in edit mode.) This magic is accomplished by the rendered expression on
each button in the cdForm.jsp. For example, Listing 6 shows the
rendered expression on the submitAdd button rendered="#{not
CDManagerBean.editMode}". The submitAdd button is bound to the addCD method using the expression (action="#{CDManagerBean.addCD}") .
Listing 6. Adding a CD using the addCD() method
[StoreController.java]
/**
* Add a cd to the store.
*
* @return outcome
*/
public String addCD() {
store.addCD(this.cd);
return "success";
}
|
Validating the fields
Before the addCD method is invoked, JSF has to validate the fields from the GUI. This is actually fairly easy, since you haven't
associated the fields with any validators. The values are
copied from the request parameters to the components' values (by the
components themselves) in the apply request value phase. At this
point, the price is converted from a string to a float. If the user
entered "abc" for the price, the conversion to float would fail, and
control would be redirect to the cdForm.jsp page for the end user
to fix. The h:message associated with the price would display a conversion error message. If all the values could be converted and were present (if required to be present), you could advance to
validation processing. Since this example application doesn't have validators
associated with the components (I'll introduce this feature in the next
article), you'll simply advance to the update model values phase.
In the update model values phase, the setter methods on the CD are invoked
with the converted and validated values stored in the GUI components.
The addCD() method is invoked in the invoke
application phase. The addCD() method uses a business delegate (a store object) to perform this operation. The addCD method uses the store object to store the CD in the system. Since the addCD method returns success, the listing will be displayed next, as defined in faces-config.xm. The navigation rule defined in
faces-config.xml is shown in Listing 7.
Listing 7. Navigation rule for the addCD successful outcome
<navigation-rule>
<from-view-id>/cdForm.jsp</from-view-id>
<navigation-case>
<from-action>#{CDManagerBean.addCD}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/listing.jsp</to-view-id>
</navigation-case>
...
</navigation-rule>
|
Use case 2: Edit a CD
The second use case for the example application also starts at the
listing page (listing.jsp). In addition to showing you how to edit data on a JSF page,
this use case will introduce you to the JSF dataTable component.
The listing page uses a dataTable to display a list of CDs. The dataTable's value is bound to the cds property of the controller class, StoreController. The cds property is defined as shown in Listing 8.
Listing 8. cds property defined in StoreController.java
[StoreController.java]
/** List of cds for CD listing. */
private DataModel cdModel = new ListDataModel();
{
cdModel.setWrappedData(store.findTitleAsc());
}
/**
* List of CDs in the system.
*
* @return Returns the cds.
*/
public DataModel getCds() {
return cdModel;
}
|
The cds property is based on a java.util.List returned from the store object StoreManagerDelegate, which is the application's business delegate. The cdModel wraps the list returned from the store in a DataModel. DataModel is the model for a dataTable.
The dataTable is defined as shown in Listing 9.
Listing 9. dataTable defined in listing.jsp
<f:view>
<h:form>
<h:dataTable id="items"
value="#{CDManagerBean.cds}"
var="cd"
rowClasses="oddRow, evenRow"
headerClass="tableHeader">
|
Notice that the value is bound to the controller's cds
property. The rowClasses and headerClass attributes are used to specify CSS classes that will be used to define the look and feel of the dataTable. As mentioned, JSF relies heavily on CSS to define the look and feel of a
GUI. If you don't know CSS (that is, you've been getting by with font tags
and HTML tables), you may want to learn it before taking the JSF
leap.
The column component
The Title, Artist, and Price fields are displayed with the column component, as shown in Listing 10 (only the Title field is shown).
Listing 10. Adding fields in the column component
<h:column>
<f:facet name="header">
...
<h:outputText value="Title"/>
</f:facet>
<h:commandLink action="#{CDManagerBean.editCD}">
<h:outputText value="#{cd.title}"/>
</h:commandLink>
</h:column>
|
The column component is a child component of the dataTable. The column component takes a single child component and a facet. A facet is a named subcomponent. A facet is not a child component. The column component has a facet called header that defines what is displayed in the header. commandLink is the child component of the column component for this example. commandLink displays the CD's title in a link that is bound to the action #{CDManagerBean.editCD}. The action attribute binds the commandLink to the editCD() method of the controller class, as shown in Listing 11.
Listing 11. Backing bean method for the editCD commandLink
[StoreController.java]
/**
* Edit the CD. This get executed before the edit cdForm
* page gets loaded.
*
* @return outcome
*/
public String editCD() {
this.cd = (CD) cdModel.getRowData();
this.cd = (CD) store.getCDById(cd.getId());
if ((cd.getCategory() != null) || !"".equals(cd.getCategory())) {
this.subCategoryList.setRendered(true);
this.subCategories = getSubcategoriesList(cd.getCategory());
} else {
this.subCategoryList.setRendered(false);
}
this.editMode = true;
return "success";
}
|
The editCD() method
The editCD() method is invoked in the invoke application phase of the JSF lifecycle. The editCD() method prepares the controller to display the cdForm.jsp page in editing mode. It does this by looking up the current selected CD by invoking the cdModel.getRowData() method.
Note that the JSF DataModel allows you to work with data at higher level of abstraction than typical Web applications. You don't have to inspect the request parameters: You simply ask the DataModel (cdModel)
which CD has been selected by invoking the cdModel.getRowData() method. This higher level of abstraction simplifies Web development considerably.
Once you have the current selected CD, you use the business delegate
to load the latest copy of that CD (store.getCDById()).
After loading the CD, store.getCDById() turns on the
subCategory list (assuming the CD is associated with a category), then sets the editMode property to true. You'll recall that the editMode property is used by cdForm to display the Add or Update buttons. Last, the store.getCDById() method returns success. The
important navigation rule shown in Listing 12 stipulates that
returning success advances you to the cdForm.jsp, as shown below.
Listing 12. An important navigation rule
<navigation-rule>
<from-view-id>/listing.jsp</from-view-id>
<navigation-case>
<from-action>#{CDManagerBean.editCD}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/cdForm.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-action>#{CDManagerBean.addNew}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/cdForm.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
The updateCD() method
The CD form loads and displays whatever the current value of the CD
property's properties are set to. The end user edits the resulting
form as needed and clicks the Update button when finished. The
Update button is the only button that shows up when the user is in Edit
mode, and it only shows up when editMode is true, as you see in Listing 13.
Listing 13. Update CD button
[cdForm.jsp]
<h:commandButton id="submitUpdate"
action="#{CDManagerBean.updateCD}"
value="Update CD"
rendered="#{CDManagerBean.editMode}"/>
|
The Update button is bound to the updateCD() method. Before the update method is invoked, JSF has to validate the fields from
the GUI. The values are copied from the request parameters to the
components' values (by the components themselves) in the apply request
value phase. At this point, the price is converted from a string into a
float. No validators are associated with the components, so if all the
required values are present and can be converted, you can move on to the next
phase of the lifecycle.
Updating the model values
In the update model values phase, the setter methods on the CD are
invoked with the converted and validated values stored in the GUI
components. The updateCD() method is invoked in the invoke application phase. The updateCD() method is shown in Listing 14.
Listing 14. updateCD() method
[StoreController.java]
/**
* Update the CD loaded on the form.
*
* @return success
*/
public String updateCD() {
store.updateCD(this.cd);
this.editMode = false;
return "success";
}
|
The updateCD() method delegates most of its responsibility to the business delegate. It sets the editMode to false, which is the default, and
returns success. The success outcome redirects you back to the listing,
where you can look at your newly edited CD based on the navigation
rule shown in Listing 15.
Listing 15. A successful UpdateCD takes you to listing.jsp
[faces-config.xml]
<navigation-rule>
<from-view-id>/cdForm.jsp</from-view-id>
...
<navigation-case>
<from-action>#{CDManagerBean.updateCD}</from-action>
<from-outcome>success</from-outcome>
<to-view-id>/listing.jsp</to-view-id>
</navigation-case>
</navigation-rule>
|
Use case 3: Sort CDs
The final use case I'll walk you through will show how to sort
tables. This use case also starts at the CD listing page. The listing
page allows CDs to be sorted by title and artist in ascending and
descending order. In this example, I'll show you how to sort by title,
leaving sorting by artist as a learning exercise.
The header for title sorts has links to sort methods on the
controller. Listing 16 shows how the title's header is listed in listing.jsp.
Listing 16. Sorting commandLinks
[listing.jsp]
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Title"/>
<f:verbatim> [</f:verbatim>
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sortTitleAsc}">
<h:outputText id="ascTitle" value="asc"/>
</h:commandLink>
<h:outputText value=","/>
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sortTitleDec}">
<h:outputText id="decTitle" value="dec"/>
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:commandLink action="#{CDManagerBean.editCD}">
<h:outputText value="#{cd.title}"/>
</h:commandLink>
</h:column>
|
The panelGroup component
Notice in Listing 16 that the links are defined inside of the header facet for the
title column. A facet only associates a single-named component; therefore, to place multiple-link components inside of the header facet, you need to use a panelGroup. A panelGroup (similar to a panelGrid) is a single component that contains a number of child components. The panelGroup contains two links, as shown in Listing 17.
Listing 17. panelGroup component links
[listing.jsp]
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sortTitleAsc}">
<h:outputText id="ascTitle" value="asc"/>
</h:commandLink>
...
<h:commandLink styleClass="smallLink"
action="#{CDManagerBean.sortTitleDec}">
<h:outputText id="decTitle" value="dec"/>
</h:commandLink>
|
The first link is bound to the controller method sortTitleAsc, and the second is bound to sortTitleDec. These methods are listed as shown in Listing 18.
Listing 18. panelGroup link methods
[StoreController.java ]
/**
* Uses the store delegate to return
* a sorted list of CDs by title (ascending).
*
* @return asc
*/
public String sortTitleAsc() {
this.cdModel.setWrappedData(store.findTitleAsc());
return "asc";
}
/**
* Uses the store delegate to return
* a sorted list of CDs by title (descending).
*
* @return dec
*/
public String sortTitleDec() {
this.cdModel.setWrappedData(store.findTitleDec());
return "dec";
}
|
Both of these methods rely on the business delegate to return a
java.util.List sorted in the correct manner. Notice that the methods return the logical outcomes asc and dec. Neither of these outcomes has a mapping in the faces-config.xml file. Outcomes that do not have mappings cause the current view to be reloaded; thus, the listing.jsp would be reloaded when these methods were called, and the list would be redisplayed in the correct order.
The beauty of this approach is that it relies on the business delegate to
do the sorting. The business delegate may in turn rely on a DAO object
that in turn relies on a database query or an OR mapping query that sorts
the CDs very efficiently. This is usually a much better approach than having
"smart" GUI components that know how to sort random domain objects (CD
is a domain object) because the sorting is often, strictly speaking, part of the
model (that is, part of the domain object), not the view.
As mentioned, the code for sorting by title and sorting by artist
is nearly the same. As a learning exercise, try writing the code for the
fourth use case yourself, but this time, sort by artist rather than by
title.
Immediate event handling
The final topic I'll cover is immediate event handling.
Immediate event handling is useful in cases where you do not want (or
need) to validate an entire page to process a user input. As
you'll recall, the example application's cdForm.jsp page uses
radio buttons to display a list of categories and subcategories. When a
category is selected by the end user, the cdForm.jsp page uses
JavaScript to post the form back so you can display a list of subcategories.
This is an example of immediate event handling, since the
whole form is not validated before the event handler is invoked.
Instead, the event handler for the category list populates the subcategories and
forces JSF to skip to the render response phase. The event handler for
components normally executes in the invoke application phase.
Event handlers for immediate event components execute in the apply
request values phase, which happens before the process validation phase of
the rest of the components.
Listing 19 shows the category list on the cdForm.jsp page
again.
Listing 19. Category list on cdForm.jsp
[cdForm.jsp]
<h:selectOneRadio id="category"
value="#{CDManagerBean.cd.category}"
immediate="true"
onclick="submit()"
valueChangeListener="#{CDManagerBean.categorySelected}">
<f:selectItems value="#{CDManagerBean.categories}"/>
</h:selectOneRadio>
|
The selectOneRadio category field is
bound to the CD's category property
(value="#{CDManagerBean.cd.category}"). Note that immediate event handling is turned on (immediate="true"). This setting means that the Category component's events will be handled (and converted and validated) in the apply request values phase, rather than the invoke application phase.
The JavaScript magic is in the line onclick="submit()" -- says that when the user makes a change, it should be immediately
submitted to the Web application for processing.
The event handler method
The available categories that show up in the listing are determined
by the f:selectItems tag's value
(value="#{CDManagerBean.categories}"). The event handler for this component changing is the controller's
categorySelected() method
(valueChangeListener="#{CDManagerBean.categorySelected}").
The event handler is shown in Listing 20.
Listing 20. categorySelected event handler
[StoreController.java]
/**
* Event Handler for a category getting selected.
*
* @param event event data
*/
public void categorySelected(ValueChangeEvent event) {
subCategoryList.setRendered(true);
String value = (String) event.getNewValue();
if (value != null) {
this.subCategories = this.getSubcategoriesList(value);
}
FacesContext context = FacesContext.getCurrentInstance();
context.renderResponse();
}
|
The first thing the categorySelected() method does is allow the subCategoryList to render itself. The
categorySelected() method then uses the selected category value to look up a list of subCategories. The
subCategories property is bound to the values of the subcategoryList. Next, the event handler forces JSF to go to the render response phase by calling the renderResponse() method on the current FacesContext. Thus, the GUI (cdForm.jsp) will redisplay with the available subcategories for the current category
displaying.
Binding a component to the controller
The subCategoryList component is bound from the GUI. Just like you can bind values to components, you can bind components into a controller. The subcategory is defined in the cdForm.jsp page, as
shown in Listing 21.
Listing 21. Subcategory list defined in the cdForm.jsp page
[cdForm.jsp]
<h:selectOneListbox id="subcategory"
value="#{CDManagerBean.cd.subCategory}"
binding="#{CDManagerBean.subCategoryList}">
<f:selectItems value="#{CDManagerBean.subCategories}"/>
</h:selectOneListbox>
|
The binding attribute allows you to bind components from the GUI into the backing bean (controller). Thus, this above component is bound to CDManagerBean.subCategoryList, which is a property in your controller defined as shown in Listing 22.
Listing 22. subCategoryList property
[StoreController.java ]
/** GUI Component that represents
the Subcategory list on the CDForm. */
private UIInput subCategoryList;
{
subCategoryList = new HtmlSelectOneListbox();
}
/**
* Subcategory list component
*
* @param aSubCategoryList The subCategoryList to set.
*
* @uml.property name="subCategoryList"
*/
public void setSubCategoryList(UIInput aSubCategoryList) {
this.subCategoryList = aSubCategoryList;
}
/**
* Subcategory list component
*
* @return Returns the subCategoryList.
*
* @uml.property name="subCategoryList"
*/
public UIInput getSubCategoryList() {
return subCategoryList;
}
|
Immediate event handling uses just a little bit of JavaScript magic
(the onclick="submit()" command) and the convenience of the flexible JSF lifecycle to let you process input from a single component, rather than waiting for the entire page to be validated. In this example,
I've shown how immediate processing of a Category
component enables you to show subcategories without reloading the
page.
Conclusion
This concludes the second article in the JSF series, where I've used
a working example and three use cases to introduce you to the JSF request
processing lifecycle and demonstrate some of the essential features of
its component model. I've also shown you how JSF combines with Struts
Tiles for a more uniform layout across JSF pages, how to work with the JSF
DataModel, and how to combine JSF and JavaScript for immediate event handling.
Part 3 will pick up where I've left off, incorporating advanced features like conversion and validation. I'll show you how to create your own custom JSF
validators and convertors, as well as how to use the built-in ones
when the situation calls for it.
Downloads | Description | Name | Size | Download method |
|---|
| Source code without JAR files | jsf-example2NoJars.zip | 91.2 KB | HTTP |
|---|
| Source code with JAR files | jsf-example2WithJars.zip | 6.26 MB | HTTP |
|---|
Resources
- Click the Code icon at the top or bottom of this page to
download the article source.
- Don't miss a single article in the
JSF for nonbelievers
series by Rick Hightower.
- Visit the JSF project page to download the JavaServer Faces APIs, custom tag library, and related documentation.
- For detailed install and build instructions for Ant and Maven, see the author's JSF resources for this series.
- You can download Maven from the Apache Maven Project.
- To learn more about Struts Tiles, see the "Master the Tiles framework" tutorial (developerWorks,
December 2003).
- Jackwind Li Guojie's "UI
development with JavaServer Faces" tutorial (developerWorks, September
2003) is an early bird's look at the technology.
- Roland Barcia's five-part "Developing JSF Applications using WebSphere
Studio V5.1.1" (developerWorks, January 2004) tutorial is a
hands-on introduction to programming with JSF.
- Srikanth Shenoy and Nithin Mallya show you how to integrate the
features of Struts, Tiles, and JavaServer Faces in the advanced article "Integrating Struts, Tiles, and JavaServer Faces"
(developerWorks, September 2003).
-
Browse for books on these and other technical topics.
- You might also want to check out the
detailed JSF tutorial from Sun Microsystems.
- You'll find articles about every aspect of Java programming in the
developerWorks Java technology zone.
- Also see the Java
technology zone for a complete lsting of free
Java-focused tutorials from developerWorks.
About the author  | |  | Rick Hightower serves as chief technology officer for ArcMind Inc.
He is co-author of the popular book Java Tools for Extreme
Programming, about applying extreme programming to J2EE development,
as well as co-author of Professional Struts. He worked on JSF QuickStart
with Warner Onstine, and some of the material in this series is based on
examples in that course. The discussion of JSF lifecycle is based on
Warner's discussion of it in the ArcMind course, with some
embellishments from Hightower. The lifecycle diagrams were created by Paul Tabor for the ArcMind JSF course; you will learn more about him when you read the third article in this series. Contact Hightower at rhightower@arc-mind.com. |
Rate this page
|