Getting started with JavaServer Faces 1.2, Part 2: JSF life cycle, conversion, validation, and phase listeners Skill Level: Introductory

Richard Hightower ([email protected]) CTO ArcMind

29 Jan 2008

This tutorial series covers how to get started with ™ Server Faces (JSF) technology, a server-side framework that offers a component-based approach to Web user-interface development. Part 1 gets you started with a JSF 1.2 overview and a basic application. This sequel gives you a firm grasp of JSF's more-advanced features: custom validators, converters, and phase listeners. Along the way you'll gain an understanding of the JSF application life cycle.

Section 1. Before you start

About this series

Get an introduction to Java™ Server Faces (JSF) technology, a server-side user-interface component framework for Java-based Web applications. This series is for developers who are new to JSF and want to come up to speed quickly — not just with JSF, but with using JSF components to reduce effort. This series covers just the essentials, with lots of examples.

JSF is a more-traditional GUI development environment like AWT, SWT, and Swing. One of its major benefits is that it makes Web development easier by putting the hard work on the framework developers, not the application developers. Granted, JSF itself is more complex than many other Web frameworks, but the complexity is

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 1 of 52 developerWorks® ibm.com/developerWorks

hidden from the application developer. It is much easier to develop Web applications in JSF than in most other frameworks: it requires less code, less complexity, and less configuration.

If you are doing Java server-side Web development, JSF is the easiest framework to learn. It is geared for creating Web applications (not Web sites). It allows you to focus on your Java code without handling request objects, session objects, or request parameters, or dealing with complicated XML files. With JSF, you can get more things done more quickly than with other Java Web frameworks.

About this tutorial

This tutorial picks up where Part 1 leaves off. If you are new to JSF or just want a refresher, then read the first installment before you begin this one. Even if you are an old JSF pro, there is likely a gem or two in that will help you out.

Although tool support is a main benefit of JSF, you won't use fancy tools or IDE support in this tutorial. This tutorial covers the essentials with just background information to keep the discussion going and to keep you productively learning to use JSF to build Web applications.

Objectives

In this tutorial, continue getting an overview of JSF's features, and learn how to work with all of the JSF components. Build a simple contact-management application — a basic CRUD (create, read, update, delete) listing. After learning about the JSF application life cycle, improve the application with custom converters and validators. The tutorial winds down with a taste of some advanced JSF programming: create an object-level validation framework using a phase listener.

Who should take this tutorial?

If you are new to JSF, this tutorial is for you. Even if you have used JSF but have not tried out the JSF 1.2 features or have only used GUI tools to build JSF applications, you will likely learn a lot from both tutorials in this series.

Prerequisites

This tutorial is written for Java developers whose experience is at a beginning to intermediate level. You should have a general familiarity with using the Java language, with some GUI development experience.

JSF life cycle, conversion, validation, and phase listeners Page 2 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

System requirements

To run the examples in this tutorial, you need a Java development environment (JDK) and . It helps to also have a Java IDE. Maven project files and Eclipse Java EE and Web Tools Project (WTP) project files are provided. See Download to obtain the example code. Visit the author's companion site (see Resources) for additional information about how to run the examples.

Section 2. Sample JSF CRUD application

This section introduces a simple CRUD application that you'll build on in subsequent sections to learn about:

• Each of JSF's standard HTML components • Creating custom converters • Working with validators • Working with phase listeners

A contact-management application

The application you'll build in this section is a contact-management application that's similar in structure to the calculator application in Part 1. As you can see in Figure 1, the application is a standard CRUD listing. It requires no navigation rules because the whole application uses a single view (the contacts.jsp page).

Figure 1. Contact-management sample application

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 3 of 52 developerWorks® ibm.com/developerWorks

Figure 2 shows the basic flow through the application:

Figure 2. Contact-management sample application, link chase

JSF life cycle, conversion, validation, and phase listeners Page 4 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

This CRUD application consists of the following elements:

• ContactController: The JSF controller • Contact: The model object that represents contact information • ContactRepository: The model object that creates, reads, updates, and deletes Contact objects • contacts.jsp: The JavaServer Pages (JSP) page that lays out the JSF component tree for managing contacts • faces-config.xml: The JSF configuration, where you declare ContactController and ContactRepository as managed beans, and inject ContactRepository into ContactController

ContactController

ContactController backs the contacts.jsp page. Listing 1 shows the code for ContactController:

Listing 1. ContactController

package com.arcmind.contact.controller; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.component.UICommand; import javax.faces.component.UIForm; import javax.faces.context.FacesContext; import com.arcmind.contact.model.Contact; import com.arcmind.contact.model.ContactRepository; public class ContactController { /** Contact Controller collaborates with contactRepository. */ private ContactRepository contactRepository; /** The current contact that is being edited. */ private Contact contact = new Contact(); /** Contact to remove. */ private Contact selectedContact; /** The current form. */ private UIForm form; /** Add new link. */ private UICommand addNewCommand; /** Persist command. */ private UICommand persistCommand; /** For injection of collaborator. */ public void setContactRepository(ContactRepository contactRepository) { this.contactRepository = contactRepository;

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 5 of 52 developerWorks® ibm.com/developerWorks

} public void addNew() { form.setRendered(true); addNewCommand.setRendered(false); persistCommand.setValue("Add"); } public void persist() { form.setRendered(false); addNewCommand.setRendered(true); if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact); } else { addStatusMessage("Updated " + contact); } } public void remove() { contactRepository.remove(selectedContact); addStatusMessage("Removed " + selectedContact); } public void read() { contact = selectedContact; form.setRendered(true); addNewCommand.setRendered(false); addStatusMessage("Read " + contact); persistCommand.setValue("Update"); } private void addStatusMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, message, null)); } //most getter/setter omitted }

Listing 1 creates a CRUD GUI in fewer than 74 lines of code — not too bad. ContactController is managed in request scope, so a new Contact is created when a ContactController is instantiated. Three components — form (of type UIForm), addNewCommand (of type UICommand), and persistCommand (of type UICommand) — are bound to the ContactController.

The addNew() method ensures that:

• form is turned on so the user can enter a new contact — form.setRendered(true) • addNewCommand is turned off — addNewCommand.setRendered(false) • persistCommand's label is set to Add — persistCommand.setValue("Add") The persist() method handles both updating an existing contact and adding a new contact using the contactRepository. The persist() method turns the

JSF life cycle, conversion, validation, and phase listeners Page 6 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

form off and the addNewCommand on. The remove() method removes the contact from the system using the contactRepository.

The read() method copies the selectedContact (of type Contact) to the contact. (The contact — also of type Contact — is value bound to the form.) You might wonder where the selectedContact comes from. It is selected when the user clicks a contact in the contact listing. (This tutorial covers it when discussing the JSP.) As in Part 1, addStatusMessage adds status messages so you can display them with .

Contacts view

The JSP page uses a (a component not covered in Part 1), as shown in Listing 2:

Listing 2. contacts.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> Contacts

Contacts

<%-- First Name --%> <%-- Last Name --%>

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 7 of 52 developerWorks® ibm.com/developerWorks

The is value bound to display the contacts from the contactController, using "#{contactController.contacts}". Each contact is mapped for the table using the var attribute: var="contact". In contacts.jsp, the oddRow and evenRow styles (defined in a CSS file) are set into the rowClasses attribute with rowClasses="oddRow, evenRow". This allows the to use alternating styles for the tables. Check the online documentation for a complete list of what can be done with and styles, because has quite a few capabilities. (See Resources for a link to the JSF API Javadocs.)

You can also set a style for the entire with styleClass="contactTable" or for its header region with headerClass="headerTable". Or you can do the same alternating styles for columns using the columnClasses attribute:

JSF life cycle, conversion, validation, and phase listeners Page 8 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

columnClasses="normal,centered". The is set up so it does not render if no contacts exist: rendered="#{not empty contactController.contacts}". The component is very versatile and easy to use.

Inside , you use s to display the values in properties. Each column defines a header in a tag. A facet is a name component that another component uses. Then, after the and inside the component, you use the component, which outputs the contact's firstName and lastName properties.

Each row has a remove link and an edit link, each of which uses a . The remove commandLink is bound to the contactController.remove method. The edit link is bound to the contactController.read method. The contactController.selectedContact property is populated with the current row because of the way you configure the . The causes the current row's contact to be copied to selectedContact before the action method is invoked.

faces-config.xml for the contacts CRUD application

The faces-config.xml file wires the ContactRepository into the ContactController, as shown in Listing 3:

Listing 3. faces-config.xml

contactRepository com.arcmind.contact.model.ContactRepository application contactController com.arcmind.contact.controller.ContactController request contactRepository com.arcmind.contact.model.ContactRepository #{contactRepository}

Notice that the contactRepository is in application scope and that it is injected into contactController's contactRepository using the

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 9 of 52 developerWorks® ibm.com/developerWorks

element. This technique lets you inject dependencies/collaborators into controllers, which aids in keeping the model and view separate, and it allows you to inject mock objects that can later be replaced with real objects. On more than one occasion I've mocked a model object like the ContactRepository and later replaced it with the real version after the GUI was complete.

The model of the application is pretty simple, as shown in Listings 4 and 5. Listing 4 shows the Contact class:

Listing 4. Contact

package com.arcmind.contact.model; public class Contact { private String firstName; private String lastName; protected long id; public Contact(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public Contact() { } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (id ^ (id >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final Contact other = (Contact) obj; if (id != other.id) return false; return true; } @Override public String toString() { return String.format("Contact: %s %s", firstName, lastName); } public long getId() { return id; } public void setId(long id) { this.id = id; } }

Listing 5 shows the ContactRepository class, which simulates writing contacts to a database:

Listing 5. ContactRepository

JSF life cycle, conversion, validation, and phase listeners Page 10 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

package com.arcmind.contact.model; import java.util.LinkedHashMap; import java.util.List; import java.util.ArrayList; import java.util.Map; public class ContactRepository { private Map contacts = new LinkedHashMap(); private static long counter = 1l; public List getContacts() { return new ArrayList(contacts.values()); } public synchronized Contact persist(Contact contact) { if (contact.id == 0) { contact.id = counter++; } return contacts.put(contact.id, contact); } public synchronized void remove(Contact contact) { contacts.remove(contact.id); } }

What you have now is a pretty basic CRUD application. In the next section, you'll build on top of this base to learn how to use the different JSF components.

Section 3. Working with JSF components

In this section, you'll use a variety of JSF components to enhance the CRUD application:

Figure 3 shows how these components appear in the GUI:

Figure 3. Contact management with a slew of common JSF components

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 11 of 52 developerWorks® ibm.com/developerWorks

Subviews

As you can imagine, it would be difficult to include all of your JSF components on one page. Fortunately, you can split JSF components into separate views using a , as shown in Listing 6:

Listing 6. Subviews contacts.jsp

Contacts (2nd version)

JSF life cycle, conversion, validation, and phase listeners Page 12 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

You can use s in the parent page or the included page (not both). In JSF 1.2, the is optional. In older versions of JSF, it is required. Some IDEs apparently expect s, so you might need to use them even if you are using JSF 1.2 or later.

Select one

In JSF, components are separated into two parts: the JSF component itself and a renderer that's responsible for displaying the component. The UISelectOne component has several renderers. It backs HtmlSelectOneListbox, HtmlSelectOneMenu, and HtmlSelectOneRadio.

The contacts application (2nd version) uses . For this to work, you add three new model objects: Group (shown in Listing 7), Tag, and ContactType. You also add two new repository objects: GroupRepository and TagRepository, which are similar to ContactRepository. The ContactType does not need a repository because it is an Enum. The Contact class now has three new properties for the group it is in (called group), the tags that are associated with it (tags), and lastly its type.

Listing 7. Subviews contacts.jsp/form.jsp

<%-- Group --%>

Note that the selectOneMenu uses the value attribute to bind the selectOneMenu to the selectedGroupId. The body of the selectOneMenu contains a , which is value bound to the groups property: value=#{contactController.groups}. You create the groups list in the backing bean. The code for the selectedGroupId property and the groups property is in Listing 8:

Listing 8. Building a list of groups

public class ContactController { ... private GroupRepository groupRepository; ... private Long selectedGroupId; ... public List getGroups() { List groups = groupRepository.list(); List list = new ArrayList(groups.size()+1);

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 13 of 52 developerWorks® ibm.com/developerWorks

list.add(new SelectItem(Long.valueOf(-1L), "select one")); for (Group group : groups) { SelectItem selectItem = new SelectItem(group.getId(), group.getName()); list.add(selectItem); } return list; } //Other getter/setters removed ...

The groups property returns a list of SelectItems. The SelectItem class is used to represent a single item in a list. It's used by both the UISelectMany and UISelectOne components. Notice that the getGroups method uses groupRepository, which is a repository object that is injected just like the contactRepository to get a list of groups. The groupRepository manages Group domain objects. A Group represents a group. A Contact represents a contact. The getGroups() method creates a list of SelectItems using the group.id property as the value and the group.name property as the label.

Notice that you add a "select one" SelectItem with a value of -1. You use this to determine if an item has been selected. If it has not, you pick up that it was not selected by using the that is in the selectOneMenu (see Listing 7 again). Also note that the selectOneMenu uses validatorMessage="required" to display a short error message.

Note that Listing 8 calls the repository directly. If the repository was really talking to a database or cache, you' want to make the call in the action method in order to do error handling there and expose the results as selected items in the property.

When the form is submitted, the selectedGroupId is set. The persist() method that is bound to the update and create buttons uses selectedGroupId to look up the group from the repository, as shown in Listing 9:

Listing 9. Updating persist() to use selectedGroupId

public class ContactController { ... public void persist() { /* Setup the group into contact. */ contact.setGroup(groupRepository.lookup(selectedGroupId)); /* Turn form off, turn link on. */ form.setRendered(false); addNewCommand.setRendered(true); /* Add a status message. */ if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact); } else { addStatusMessage("Updated " + contact); } } ... public void read() {

JSF life cycle, conversion, validation, and phase listeners Page 14 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

/* Prepare selected contact. */ contact = selectedContact; /* Turn form on and the link off. */ form.setRendered(true); addNewCommand.setRendered(false); /* Prepare selected group id. */ selectedGroupId = contact.getGroup().getId(); ... this.selectedTagIds = tagIds.toArray(new Long[tags.size()]); addStatusMessage("Read " + contact); persistCommand.setValue("Update"); } ...

Notice that the Contact class adds a group property. Thus Contact has a many-to-one relationship with Group.

The read() method in Listing 9 initializes this.selectedTagIds so that it shows as selected in the view.

You've seen how you can handle a one-to-one relationship with . Contact also has a many-to-many relationship to Tags, represented with the tags property of Contact. To handle this, use a , as shown in Listing 10:

Listing 10. selectManyCheckbox for Contact.tags

The main difference with the is that it is bound to an array of longs, instead of a single long, as shown in Listing 10.

The Java code is similar to Listing 8, except now you are dealing with an array of selected IDs (longs), instead of a single long, as shown in Listing 11:

Listing 11. Java code to back the selectManyCheckbox

public class ContactController { ... private Long[] selectedTagIds; private TagRepository tagRepository; ... public List getAvailableTags() { List tags = tagRepository.list(); List list = new ArrayList(tags.size()); for (Tag tag : tags) { SelectItem selectItem = new SelectItem(tag.getId(), tag.getName());

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 15 of 52 developerWorks® ibm.com/developerWorks

list.add(selectItem); } return list; } ... public void persist() { ... /* Setup the tags into contact. */ List tags = new ArrayList(selectedTagIds.length); for (Long selectedTagId : selectedTagIds) { tags.add(tagRepository.lookup(selectedTagId)); } contact.setTags(tags); ... } ... public void read() { ... /* Prepare selected tag IDs. */ List tags = contact.getTags(); List tagIds = new ArrayList(tags.size()); for (Tag tag : tags) { tagIds.add(tag.getId()); } this.selectedTagIds = tagIds.toArray(new Long[tags.size()]); ... } ...

The persist() method uses the tagRepository.lookup() method to look up the Tag domain objects based on the selectedTagIds, and then sets the contact.tags property with the looked-up values. The read() method initializes the selectedTagIds based on which Tags are in the contact.tags property.

The UISelectMany class backs , but it also backs and . You can use them instead of the , as shown in Listings 12 and 13, and Figure 4.

Listing 12.

Listing 13.

Figure 4: and

JSF life cycle, conversion, validation, and phase listeners Page 16 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

Notice that the setup for all three tags is the same.

JSF 1.2 has converters for Enums, so you don't need to look up the values. You can use them directly and bind to them without having an intermediate property in the controller as you had for tags and group (see Listing 8 and Listing 11). The Contact class has an enum property, as shown in Listing 14:

Listing 14. Contact using enum for type property

public enum ContactType { BUSINESS, PERSONAL; public String toString () { return this.name().toLowerCase(); } } ... public class Contact implements Serializable { ... private Group group; private List tags; ... private ContactType type = ContactType.PERSONAL; ... public ContactType getType() { return type; } public void setType(ContactType type) { this.type = type; } ... }

Since JSF has a converter for Enums, you can bind directly to the type property, as shown in Listing 15:

Listing 15. Binding directly to contact.type

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 17 of 52 developerWorks® ibm.com/developerWorks

Listing 15 also demonstrates the use of , which creates single values. JSF's built-in converter for Enums expects the values to be the string values — the names, really, of the Enums. You can also use a , as shown in Listing 16:

Listing 16. Using

The contact-management application's other components

The contacts CRUD application also demonstrates the use of and , as shown in Listing 17:

Listing 17. Using and

... <%-- active --%> <%-- Description --%> ...

The has two additional attributes that are set with cols="80" rows="5". The bindings work as before.

Listing 18 shows the properties that you bind to in Listing 17:

Listing 18. Contact class

JSF life cycle, conversion, validation, and phase listeners Page 18 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

public class Contact implements Serializable { ... private String description; private boolean active; protected long id; ... public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } public long getId() { return id; } public void setId(long id) { this.id = id; } ... }

You bind the id, description, type, firstName, and lastName properties from Contact directly to the UI. The group and tags properties can't be bound directly because they have no JSF converter. In a later section of this tutorial ("JSF data converters"), you'll learn about converters and create some for the application. Between now and then, let's take a brief a side trip into the JSF application life cycle.

Section 4. JSF application life cycle

You can (contrary to popular belief) write 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. But understanding certain fundamentals will make your development efforts much more rewarding — and a lot less time consuming. This section takes a detour away from the contacts application and walks you through the six phases of the JSF request-processing life cycle. Take a look at what happens in each phase and how the phases interconnect. This discussion will inform the work you do in the remaining sections of the tutorial.

Phases of the JSF application life cycle

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 19 of 52 developerWorks® ibm.com/developerWorks

The six phases of the JSF application life cycle are:

1. Restore view

2. Apply request values; process events

3. Process validations; process events

4. Update model values; process events

5. Invoke application; process events

6. 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 life cycle is hardly set in stone. You can change the order of execution by skipping phases or leaving the life cycle altogether. For example, if an invalid request value is copied to a component, the current view would be redisplayed, and some of the phases might not execute.

You can also opt to leave JSF altogether, perhaps delegating to a servlet or another application framework. In this case, you could issue a FacesContext.responseComplete method invocation to redirect the user to a different page or Web resource, then use the request dispatcher (retrieved from the request object in the FacesContext) to forward to an appropriate Web resource. Alternatively, you could call FacesContext.renderResponse to re-render the original view.

The point is to let the life cycle structure your development efforts without feeling completely tied to it. You can alter the default life cycle when needed without fear of breaking your application. In most cases, you'll find that the JSF life cycle is worth adhering to because it's quite logical.

Forms must be validated before any application logic can be executed, and field data must be converted before being validated. Sticking to the life cycle 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 life cycles; they just don't tend to be as well advertised or documented.

Some developers using JSF may never write a component or extend the framework, while others may focus on just those tasks. Although the JSF life cycle 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 life cycle:

JSF life cycle, conversion, validation, and phase listeners Page 20 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

• 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 life cycle:

• Restore view • Render response In the remainder of this section, you'll walk through each phase of the JSF request processing life cycle, including event handling and validation. Before you get started, take a look at Figure 5, a diagram of the JSF application life cycle, for an overview:

Figure 5. JSF application life cycle

Phase 1: Restore view

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 21 of 52 developerWorks® ibm.com/developerWorks

In the first phase of the JSF life cycle — restore view — a request comes through the FacesServlet servlet. The servlet 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 life cycle 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 stores the state information JSF needs to manage the GUI component's state for the current request. 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 is populated as the JSP page is processed. From an initial view, JSF advances directly to the render response phase.

In the case of a postback (the user returns to a page he or 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.

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. The value from the request parameter for many components is stored in the component's submittedValue.

If a component's immediate event-handling property is set to true, the values are converted to the proper type and validated (more on conversion in the next phase). 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.

JSF life cycle, conversion, validation, and phase listeners Page 22 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

Phase 3: Process validation

Conversion and validation normally happen in the process validation phase. The component converts the component's submittedValue and stores it. So if the field is bound to an Integer property (for example), 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.

The first event handling of the life cycle takes place after the apply request values phase. At this stage, each component's values are validated against the application's validation rules. The validation rules can be predefined (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 as invalid. If a component is marked as invalid, JSF advances to the render response phase and skips the rest of the phases, which will display the current view with the validation error messages. If no validation errors occur, JSF advances to the update model values phase.

Phase 4: Update model values

The fourth phase of the JSF application life cycle — update model values — updates the server-side model's actual values by updating the properties of your managed beans. Only bean properties that are bound to a component's value are updated. Notice that this phase happens after validation, so you can be sure that the values copied to your beans' properties are valid (at least at the form-field level; they could still be invalid at the business-rule level).

Phase 5: Invoke application

At the fifth phase of the life cycle — 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.

It is during this phase that your action-handler methods are called, such as the persist() method and the read() methods in the sample application's ContactController.

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 must

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 23 of 52 developerWorks® ibm.com/developerWorks

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 life cycle. JSF takes the object returned from the action method and calls its toString() method. Then it uses this value as the outcome for the navigation rules. (Part 1 covers configuring navigation rules.)

Phase 6: Render response

In the sixth phase of the life cycle — render response — you display the view with all of its components in their current state.

Figure 6 is an object state diagram of the six phases of the JSF application life cycle, including validation and event handling:

Figure 6. The six-phase progression of the JSF application lifecycle

Section 5. JSF data converters

Conversion is the process of ensuring data is of the right object or type, hence you convert string values into other types such as Date objects, primitive float, or Float objects. You can use built-in converters or write custom converters. This section shows you JSF's standard converters and then covers custom converters in depth.

JSF's standard converters

JSF life cycle, conversion, validation, and phase listeners Page 24 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

JSF supplies many standard data converters, and most data conversions happen automatically. Table 1 shows the converter IDs and corresponding implementation classes JSF uses for simple data conversion.

Table 1. Standard JSF converters Converter Implementation class javax.faces.BigDecimal javax.faces.convert.BigDecimalConverter javax.faces.BigInteger javax.faces.convert.BigIntegerConverter javax.faces.Boolean javax.faces.convert.BooleanConverter javax.faces.Byte javax.faces.convert.ByteConverter javax.faces.Character javax.faces.convert.CharacterConverter javax.faces.DateTime javax.faces.convert.DateTimeConverter javax.faces.Double javax.faces.convert.DoubleConverter javax.faces.Float javax.faces.convert.FloatConverter

Thus if you bind to an int or an Integer, the conversion happens automatically. Listing 19 shows a component for the contact-management sample application binding directly to age. #{contactController.contact.age}:

Listing 19. Binding to age: JSF converts automatically

<%-- age --%>

JSF does this for all primitive, wrapper, String, and Enum properties. Nice. It has converters for dates and numbers as well. Numbers can have many formats, so it has a converter that allows you to describe the format that the end user will use. Ditto for dates. Listing 20 shows the use of JSF converters to convert a date using a custom format:

Although JSF handles primitives and the like quite nicely by default, when dealing with date data, you must specify the conversion tag. This tag is based on the java.text package and uses short, long, and custom patterns. Listing 20 demonstrates how to use to ensure the user's birth date is convertible into a date object, formatted as MM/yyyy (month/year). See the Java API documentation for java.text.SimpleDateFormat for a list of patterns (see Resources).

Listing 20. Specifying format for dates

<%-- birthDate --%>

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 25 of 52 developerWorks® ibm.com/developerWorks

Introducing JSF custom converters

Custom data conversion is necessary if you need to convert field data into an application-specific value object, as in the following examples:

• String to PhoneNumber object (PhoneNumber.areaCode, PhoneNumber.prefix, ...) • String to Name object (Name.first, Name.last) • String to ProductCode object (ProductCode.partNum,ProductCode.rev, ...) • String to Group • String to Tags To create a custom converter, you must:

1. Implement the Converter interface (also known as. javax.faxes.convert.Converter).

2. Implement the getAsObject() method, which converts a field (string) into an object (for example, PhoneNumber).

3. Implement the getAsString method, which converts an object (for example, PhoneNumber) into a string.

4. Register your custom converter in the Faces context.

You can see in Figure 7 how these steps fit into the JSF application life cycle:

Figure 7. Custom converter getAsObject() and getAsString() methods

JSF life cycle, conversion, validation, and phase listeners Page 26 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

In Figure 7, JSF calls the custom converter's getAsObject() method during the process validation phase. This is where the converter must convert the request string value into the desired object type and then return the object for storage in the corresponding JSF component. When the value is rendered back to the view, JSF will call the getAsString method in the render response phase. This means the converter is also responsible for transforming the object data back into a string representation.

Implement the Converter interface

Recall that the sample application's Contact domain object has a many-to-one relationship with Group and a many-to-many relationship with Tag. Previously (see Listing 9 and Listing 11) you did the conversion from id values to domain objects in the ContactController. Instead of binding directly to domain properties in the view, you bound to id fields in the ContactController. If you use JSF converters, you can get rid of a lot of that code and simplify the controller and view.

Listings 21 and 22 implement the Converter interface for the Group and Tag converters, respectively:

Listing 21. Group converter implementing Converter interface

package com.arcmind.contact.converter; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.application.FacesMessage; import com.arcmind.contact.model.Group;

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 27 of 52 developerWorks® ibm.com/developerWorks

import com.arcmind.contact.model.GroupRepository; public class GroupConverter implements Converter { ... }

Listing 22. Tag converter implementing Converter interface

package com.arcmind.contact.converter; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import com.arcmind.contact.model.Tag; import com.arcmind.contact.model.TagRepository; public class TagConverter implements Converter { ... }

Implement the getAsObject() method

The next step is to implement the getAsObject() method, which converts a field (string) into an object. Listing 23 shows the getAsObject() method for GroupConverter:

Listing 23. getAsObject() method for GroupConverter

... public class GroupConverter implements Converter { public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { GroupRepository repo = (GroupRepository) facesContext .getExternalContext().getApplicationMap() .get("groupRepository"); Long id = Long.valueOf(value); if (id == -1L) { throw new ConverterException(new FacesMessage( FacesMessage.SEVERITY_ERROR, "required", "required")); } return repo.lookup(id); } ... }

Notice that GroupConverter looks up the groupRepository and uses the groupRepository to read the Group from the repository. Also, notice that it checks to see if the value is -1L, and if it is, it puts out a required message by throwing a new ConverterException.

JSF life cycle, conversion, validation, and phase listeners Page 28 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

The TagConverter is similar. It uses the tagRepository to look up the tag value. Listing 24 shows the getAsObject() method for TagConverter:

Listing 24. getAsObject() method for TagConverter

... public class TagConverter implements Converter { public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { TagRepository repo = (TagRepository) facesContext .getExternalContext().getApplicationMap() .get("tagRepository"); return repo.lookup(Long.valueOf(value)); } ... }

Neither converter does much error checking. If your repository object were actually talking to a database or cache server, you'd probably want to wrap the getAsObject() method in a try/catch block, and add a FacesMessage with severity of SEVERITY_FATAL if a problem occurs with the database — similarly to the way you handle the -1L/required approach in the GroupConverter in Listing 23.

Implement the getAsString method

JSF needs to display the currently selected value. It does this by calling the getAsString() method of the Converter. Listing 25 implements the getAsString() method for GroupConverter:

Listing 25. getAsString() method for GroupConverter

... public class GroupConverter implements Converter { ... public String getAsString(FacesContext facesContext, UIComponent component, Object value) { return value == null ? "-1" : "" + ((Group) value).getId(); } }

Listing 26 implements the getAsString() method for TagConverter:

Listing 26. getAsString() method for TagConverter

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 29 of 52 developerWorks® ibm.com/developerWorks

... public class TagConverter implements Converter { ... public String getAsString(FacesContext facesContext, UIComponent component, Object value) { return value == null ? "-1" : "" + ((Tag) value).getId(); } }

Register your custom converter in the Faces context

After you write your converters, you need JSF to use them every time it sees a value binding that results in a Group or Tag. You do this by registering your converters in the faces-config.xml file using the element, as shown in Listing 27:

Listing 27. Registering converters with faces-config.xml

com.arcmind.contact.model.Group com.arcmind.contact.converter.GroupConverter com.arcmind.contact.model.Tag com.arcmind.contact.converter.TagConverter

Listing 27 specifies the converter class with the element and the class you're providing conversion for with the element.

Making converters work with a list of tags

Sadly, the converters do not work with generics such as List. JSF does not allow conversion to generic lists. (It should. The Java Persistence API [JPA] has no problem handling List for defining relationships. JPA and JSF 1.2 came out around the same time for Java EE 5, so you'd think that both would support generics.) To work around this problem, you can use arrays. Listing 28 shows the use of a Tag array, instead of List:

Listing 28. Using arrays instead of generics

JSF life cycle, conversion, validation, and phase listeners Page 30 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

public class Contact implements Serializable { ... private List tags; public Tag[] getTags() { if (tags != null) { return tags.toArray(new Tag[tags.size()]); } else { return null; } } public void setTags(Tag[] tags) { this.tags = Arrays.asList(tags); }

Section 6. JSF validators

The main purpose of conversion and validation is to ensure values have been properly sanitized before updating model data. Subsequently, when the time comes to invoke application methods to do something with that data, you can safely make certain assumptions about the state of your model. Conversion and validation allow you to focus on business logic rather than the tedious qualifications of input data such as null checks, length qualifiers, range boundaries, and so on.

It makes sense, then, that conversion and validation processes happen before component data is bound to your managed bean model in the update model data life cycle phase. As you saw in the "JSF application life cycle" section, conversion and validation occur in the process validation phase — conversion first, then validation.

There are four forms of validation within JSF:

• Built-in validation components • Application-level validation • Validation methods in backing beans (inline) • Custom validation components (which implement the Validator interface) This section explains and demonstrates each of these forms.

Standard validation

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 31 of 52 developerWorks® ibm.com/developerWorks

JSF supplies three standard validation components:

• DoubleRangeValidator: Component's local value must be numeric type; must be in range specified by minimum values, maximum values, or both. • LongRangeValidator: Component's local value must be numeric type and convertible to long; must be in range specified by minimum values, maximum values, or both. • LengthValidator: Type must be string; length must be in range specified by minimum values, maximum values, or both. In the example application, a contact's age can be any valid integer. Because it doesn't make sense to allow an age of, say, -2, you'll probably want to add some validation to the field. Listing 29 shows some simple validation code using for ensuring the model integrity of data in an age field:

Listing 29. Validating age for reasonable values using

<%-- age --%>

Once you have the age field sorted out, you might want to specify length restrictions on the First Name field, as shown in Listing 30.

Listing 30. Making sure firstName isn't too long or too short

<%-- First Name --%>

Although it works for many scenarios, JSF's built-in validation is somewhat limited. When you're dealing with e-mail validation, phone numbers, URLs, dates, and so on, it's sometimes better to write your own validator; that is discussed later in this section. You can also use validators provided by Tomahawk, Shale, JSF-Validations, and Crank (see Resources).

Application-level validation

JSF life cycle, conversion, validation, and phase listeners Page 32 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

In concept, application-level validation is really business-logic validation. JSF separates form- or field-level validation from business-logic validation. Basically, application-level validation entails adding code to the managed-bean methods that use the model to qualify the data already bound to your model. In the case of a shopping cart, form-level validation might validate whether a quantity entered is valid, but you would need business-logic validation to check whether the user had exceeded his or her credit limit. This is another example of the separation of concerns in JSF.

For example, let's say the user clicks a button that is bound to an action method, which is invoked during the invoke-application phase (refer back to Figure 5 for details). Prior to performing any manipulation of model data, which was presumably updated during the update model phase, you could add code that checks to see if the data entered is valid based on the application's business rules.

For instance, in the example application, the user clicks the Update/Add button, which is bound to the persist() method of the application controller. You could add validation code to the persist() method to determine whether the firstName/lastName combination already exists in the system. In cases where the contact already exists, you could also add a message to the FacesContext and then direct JSF to stay on the current view by returning null for an outcome (if navigation rules apply to this action).

Let's revisit the contacts application and perform some application-level logic in the persist() action method, as shown in Listings 31 and 32. Listing 31 shows the application-level validation logic in the controller:

Listing 31. Application-level validation logic in controller

public class ContactController { public String persist() { /* Perform the application level validation. */ try { contact.validate(); } catch (ContactValidationException contactValidationException) { addErrorMessage(contactValidationException.getLocalizedMessage()); return null; }

/* Turn form off, turn link on. */ form.setRendered(false); addNewCommand.setRendered(true);

/* Add a status message. */ if (contactRepository.persist(contact) == null) { addStatusMessage("Added " + contact); } else { addStatusMessage("Updated " + contact); } return "contactPersisted"; }

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 33 of 52 developerWorks® ibm.com/developerWorks

private void addErrorMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( FacesMessage.SEVERITY_ERROR, message, null)); }

In Listing 31, you can see that the persist() method calls validate() on the contact object. It captures any exceptions and converts the exception error message into a FacesMessage. If an exception occurs, it returns an outcome of null, meaning stay on the current view and do not navigate to the next view.

The actual validation code is in the model — that is, the Contact class's validate()method, as shown in Listing 32. This is important: as you add more validation rules to the contact, you don't need to change the controller or the view layers.

Listing 32. Validation is in the model, not the controller

... public class Contact implements Serializable { ... public void validate() throws ContactValidationException { if ( (homePhoneNumber == null || "".equals(homePhoneNumber)) && (workPhoneNumber == null || "".equals(workPhoneNumber)) && (mobilePhoneNumber == null || "".equals(mobilePhoneNumber)) ){ throw new ContactValidationException("At least one phone number" + "must be set"); } }

Application-level validation is clearly straightforward and easy to do. Its advantages are:

• Easy to implement • No need for a separate class (custom validator) • No need for page author to specify validator The disadvantages of application-level validation are that it occurs after other forms of validation (standard, custom, and component), and error messages show up only after other forms of validation have occurred.

Ultimately, application-level validation should be used only for circumstances requiring business-logic validation.

Custom validators in backing beans

JSF life cycle, conversion, validation, and phase listeners Page 34 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

You need to build your own custom validation components for data types that aren't supported by the standard JSF validators, including e-mail addresses and ZIP codes. You also need to build your own validators in cases where you want explicit control over the validation messages displayed to the end user. With JSF, you can create pluggable validation components that are reusable throughout your Web applications.

As an alternative to creating a separate validator class, you can simply implement custom validation in a backing bean method. This is better for application developers. For instance, you might write a method in a managed bean to validate phone numbers, as shown in Listing 33:

Listing 33. Phone-number validation

public class ContactValidators { private static Pattern phoneMask; static { String countryCode = "^[0-9]{1,2}"; String areaCode = "(|-|\\(){1,2}[0-9]{3}(|-|\\)){1,2}"; String prefix = "(|-)?[0-9]{3}"; String number = "(|-)[0-9]{4}$"; phoneMask = Pattern.compile(countryCode + areaCode + prefix + number); } public void validatePhone(FacesContext context, UIComponent component, Object value) throws ValidatorException { String sValue = (String)value; Matcher matcher = phoneMask.matcher(sValue); if (!matcher.matches()) { FacesMessage message = new FacesMessage(); message.setDetail("Phone number not valid"); message.setSummary("Phone number not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } } ... //ADD MORE VALIDATION METHODS FOR THE APP HERE! }

The ContactValidators class has a validatePhone() method. The validatePhone() method uses the Java regex API to make sure that the string entered is a valid phone number. If the value does not match the pattern, then the validatePhone() method throws a ValidatorException.

To use the ContactValidators class, you register it in the faces-config.xml file, as shown in Listing 34:

Listing 34. Registering ContactValidators as a managed bean

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 35 of 52 developerWorks® ibm.com/developerWorks

contactValidators com.arcmind.contact.validators.ContactValidators application

To use the validator you use the validator attribute, as shown in Listing 35, for work, home, and mobile phone-number entries:

Listing 35. Using the validator in the view with the validator attribute

<%-- Work --%> <%-- Home --%> <%-- Mobile --%>

As you can see, you bind the validatePhone() method to the components:

Using a managed bean for validation is good for application developers. However, if you're developing a reusable framework or a reusable set of components, it might be better to create standalone custom validators.

Standalone custom validators

With JSF, you can create pluggable validation components that are reusable throughout your Web applications.

To create a custom validator, you perform the following steps:

1. Create a class that implements the Validator interface (javax.faces.validator.Validator).

2. Implement the validate() method.

JSF life cycle, conversion, validation, and phase listeners Page 36 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

3. Register your custom validator in the faces-config.xml file.

4. Use the tag in your JSPs.

Let's look at these steps one by one, including the example code for creating a custom validator.

Step 1: Implement the Validator interface

The first step is to implement the Validator interface, as shown in Listing 36:

Listing 36. Implement the Validator interface

package com.arcmind.validators; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ZipCodeValidator implements Validator { /** Accepts zip codes like 85710 */ private static final String ZIP_REGEX = "[0-9]{5}"; /** Optionally accepts a plus 4 */ private static final String PLUS4_OPTIONAL_REGEX = "([ |-]{1}[0-9]{4})?"; private static Pattern mask = null; static { mask = Pattern.compile(ZIP_REGEX + PLUS4_OPTIONAL_REGEX); }

Step 2: Implement the validate() method

Next, you need to implement the validate() method, as shown in Listing 37:

Listing 37. Implement the validate() method

public class ZipCodeValidator implements Validator { ... public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { /* Get the string value of the current field */ String zipField = (String) value; /* Check to see if the value is a zip code */ Matcher matcher = mask.matcher(zipField);

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 37 of 52 developerWorks® ibm.com/developerWorks

if (!matcher.matches()) { FacesMessage message = new FacesMessage(); message.setDetail("Zip code not valid"); message.setSummary("Zip code not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } } }

Step 3: Register your custom validator

The code to register the custom validator with the FacesContext, as shown in Listing 38, should look familiar to you by now:

Listing 38. Register your custom validator in faces-config.xml

arcmind.zipCode com.arcmind.validators.ZipCodeValidator

Step 4: Use the tag in your JSPs

The tag declares use of the zipCode validator, as shown in Listing 39:

Listing 39. Use tag in your JSPs

<%-- zip --%>

Overall, creating custom validators is fairly straightforward and makes validation reusable across many applications. The downside is that you do have to create another class and manage validator registration within the faces context. However, you can take the implementation of your custom validator one step further and make it look like built-in validation by creating a custom tag that uses the validator. For common validation concerns, such as e-mail validation, this approach can support design philosophies where code reuse and consistent application behavior are of utmost importance.

Validation and conversion redux

JSF life cycle, conversion, validation, and phase listeners Page 38 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

By the time you get to the validation phase, the conversion has already happened. If, for example, you have an int property bound to an inputText field, then the field will be converted before you get to validate it.

Suppose you have a PhoneNumber value object, and instead of using Strings to store phone numbers in the Contact, you use PhoneNumber. Then the validation rule in Listing 33 for phone number would not make so much sense. In fact, that validation rule only proved that the String was in the format of a phone number. That logic should be really be part of the converter, as demonstrated by Listing 40:

Listing 40. Conversion and validator redux: PhoneConverter

package com.arcmind.contact.converter; import java.util.regex.Pattern; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import com.arcmind.contact.model.PhoneNumber; /** * @author Richard Hightower * */ public class PhoneConverter implements Converter { private static Pattern phoneMask; static { String countryCode = "^[0-9]{1,2}"; String areaCode = "( |-|\\(){1,2}[0-9]{3}( |-|\\)){1,2}"; String prefix = "( |-)?[0-9]{3}"; String number = "( |-)[0-9]{4}$"; phoneMask = Pattern.compile(countryCode + areaCode + prefix + number); } public Object getAsObject(FacesContext context, UIComponent component, String value) { System.out.println("PhoneConverter.getAsObject()"); if (value.isEmpty()) { return null; } /* Before we parse, let's see if it really is a phone number. */ if (!phoneMask.matcher(value).matches()) { FacesMessage message = new FacesMessage(); message.setDetail("Phone number not valid"); message.setSummary("Phone number not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ConverterException(message); } /* Now let's parse the string and populate a phone number object. */ PhoneNumber phone = new PhoneNumber(); phone.setOriginal(value); String[] phoneComps = value.split("[ ,()-]"); String countryCode = phoneComps[0]; phone.setCountryCode(countryCode); if ("1".equals(countryCode) && phoneComps.length == 4) {

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 39 of 52 developerWorks® ibm.com/developerWorks

phone.setAreaCode(phoneComps[1]); phone.setPrefix(phoneComps[2]); phone.setNumber(phoneComps[3]); } else if ("1".equals(countryCode) && phoneComps.length != 4) { throw new ConverterException(new FacesMessage( "No Soup for you butter fingers!")); } else if (phoneComps.length == 1 && value.length() > 10){ phone.setCountryCode(value.substring(0,1)); phone.setAreaCode(value.substring(1,4)); phone.setPrefix(value.substring(4,7)); phone.setNumber(value.substring(7)); } else { phone.setNumber(value); } return phone; } public String getAsString(FacesContext context, UIComponent component, Object value) { System.out.println("PhoneConverter.getAsString()"); return value.toString(); } }

The nice thing about converters, unlike validators, is that you can register them in faces-config.xml (see Listing 27) so that the converter is tied to a particular class. Any time that class shows up in an expression language (EL) value binding, the converter is used automatically; you don't need to add a to the JSP. The new phone number converter is automatically applied to PhoneNumbers, and you don't need to specify the converter in the view.

Since the old phone number validation is part of the phone number conversion, you might ask what a phone number validator would look like now. This question can be answered by writing a validator, shown in Listing 41, that proves a phone number is from Arizona:

Listing 41. Make sure the phone number is in AZ

package com.arcmind.contact.validators; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.ValidatorException; import com.arcmind.contact.model.PhoneNumber;

public class ContactValidators { public void validatePhone(FacesContext context, UIComponent component, Object value) throws ValidatorException { System.out.println("ContactValidators.validatePhone()"); PhoneNumber phoneNumber = (PhoneNumber)value; if (!phoneNumber.getAreaCode().equals("520") && !phoneNumber.getAreaCode().equals("602")) { FacesMessage message = new FacesMessage();

JSF life cycle, conversion, validation, and phase listeners Page 40 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

message.setDetail("Arizona residents only"); message.setSummary("Arizona residents only"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } } }

Notice that this phone number validator, unlike the previous one, does not deal with Strings. By the time it gets called, the converter has already been called. Thus, the value is not a String but a PhoneNumber.

Section 7. Working with phase listeners

PhaseListener, according to the JSF API documentation (see Resources), is "an interface implemented by objects that wish to be notified at the beginning and ending of processing for each standard phase of the request processing lifecycle" (Sun Microsystems Inc., 2006). Now that you've written some converters, validators, and action methods, you'll write some phase listeners in this section. Prior to JSF 1.2, PhaseListeners were globally defined. In JSF 1.2, PhaseListener events can be registered for at the view level or using the tag.

Implementing phase listeners

To implement a PhaseListener, you implement the PhaseListener interface, as shown in Listing 42:

Listing 42. DebugPhaseListener

package com.arcmind.phase; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; @SuppressWarnings("serial") public class DebugPhaseListener implements PhaseListener { public void beforePhase(PhaseEvent phaseEvent) { System.out.println("------BEFORE PHASE " + phaseEvent.getPhaseId()); } public void afterPhase(PhaseEvent phaseEvent) { System.out.println("------AFTER PHASE " + phaseEvent.getPhaseId());

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 41 of 52 developerWorks® ibm.com/developerWorks

if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE) { System.out.println("REQUEST END\n\n"); } } public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }

The PhaseListener notifies you before and after each JSF phase. You tell JSF which phases you are interested in with the getPhaseId method. PhaseId.ANY_PHASE implies that you want to be notified before and after each phase. The DebugPhaseListener prints out the phase event name so you can see where things happen. I've added System.out.println statements to every validation, conversion, and action method. I also added a System.out.println to the firstName property's getter/setter so you can see when it's accessed.

Next, you must register the phase listener in faces-config.xml, as shown in Listing 43:

Listing 43. Registering DebugPhaseListener in faces-config.xml

com.arcmind.phase.DebugPhaseListener

Phase-listener output

Listing 44 shows what happens when you load the form for the first time:

Listing 44. Output of DebugPhaseListener when form first loads

------BEFORE PHASE RESTORE_VIEW 1 ------AFTER PHASE RESTORE_VIEW 1 ------BEFORE PHASE RENDER_RESPONSE 6 ContactController.getContacts() ------AFTER PHASE RENDER_RESPONSE 6 REQUEST END

Based on the code in Listing 42, JSF sees that this is an initial request for a view, uses the JSP to build the view, and then goes immediately to the render response phase. Notice that the controller's getContacts() method is called during the render response phase.

When you click the Add New link, you see the output shown in Listing 45:

JSF life cycle, conversion, validation, and phase listeners Page 42 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

Listing 45. Output of DebugPhaseListener after the addNew() method is called

------BEFORE PHASE RESTORE_VIEW 1 ------AFTER PHASE RESTORE_VIEW 1 ------BEFORE PHASE APPLY_REQUEST_VALUES 2 ------AFTER PHASE APPLY_REQUEST_VALUES 2 ------BEFORE PHASE PROCESS_VALIDATIONS 3 ------AFTER PHASE PROCESS_VALIDATIONS 3 ------BEFORE PHASE UPDATE_MODEL_VALUES 4 ------AFTER PHASE UPDATE_MODEL_VALUES 4 ------BEFORE PHASE INVOKE_APPLICATION 5 ContactController.addNew() ------AFTER PHASE INVOKE_APPLICATION 5 ------BEFORE PHASE RENDER_RESPONSE 6 Contact.getFirstName() ContactController.getGroups() ContactController.getGroups() ContactController.getAvailableTags() ContactController.getContacts() ------AFTER PHASE RENDER_RESPONSE 6 REQUEST END

The addNew() method causes the form to render. Since the addNew() is a postback, JSF goes through all of the phases. Since the form was not rendered before the addNew() method is called, its fields are not processed before the addNew() method is called.

Next, enter a bad ZIP code (such as aaa) and a bad phone number (such as aaa). Then select two tags and a group, and hit the Add button. You'll see the output shown in Listing 46:

Listing 46. Output after entering a bad zip, bad phone, a group, and some tags

------BEFORE PHASE RESTORE_VIEW 1 ------AFTER PHASE RESTORE_VIEW 1 ------BEFORE PHASE APPLY_REQUEST_VALUES 2 ------AFTER PHASE APPLY_REQUEST_VALUES 2 ------BEFORE PHASE PROCESS_VALIDATIONS 3 GroupConverter.getAsObject ContactController.getAvailableGroups() ZipCodeValidator.validate() ContactValidators.validatePhone() TagConverter.getAsObject TagConverter.getAsObject ContactController.getAvailableTags() ContactController.getAvailableTags() ------AFTER PHASE PROCESS_VALIDATIONS 3 ------BEFORE PHASE RENDER_RESPONSE 6 ContactController.getAvailableGroups() GroupConverter.getAsString GroupConverter.getAsString GroupConverter.getAsString ContactController.getAvailableTags() TagConverter.getAsString TagConverter.getAsString TagConverter.getAsString TagConverter.getAsString ContactController.getContacts() ------AFTER PHASE RENDER_RESPONSE 6 REQUEST END

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 43 of 52 developerWorks® ibm.com/developerWorks

In the process validation phase, the GroupConverter.getAsObject() method is called for the group that was selected. TagConverter.getAsObject() is called twice, once for each tag that was selected. Both the custom validators you wrote — ZipCodeValidator.validate() and ContactValidators.validatePhone() — are called in the process validation phase. In the render response phase the converters' getAsString() methods get called per object in the list for available groups and available tags.

Now you can imagine if ContactController.getAvailableGroups() and ContactController.getAvailableTags() hit the database each time that you would be hitting the database four times to get these lists. You would want to add some logic so that you would hit the database only once per request. You could, for example, load the data from the action only if it was not already loaded.

A discussion of JSF phases would not be complete without showing the output from submitting a valid form, as shown in Listing 47:

Listing 47. All the way through

------BEFORE PHASE RESTORE_VIEW 1 ------AFTER PHASE RESTORE_VIEW 1 ------BEFORE PHASE APPLY_REQUEST_VALUES 2 ------AFTER PHASE APPLY_REQUEST_VALUES 2 ------BEFORE PHASE PROCESS_VALIDATIONS 3 GroupConverter.getAsObject ContactController.getAvailableGroups() ZipCodeValidator.validate() ContactValidators.validatePhone() ContactValidators.validatePhone() ContactValidators.validatePhone() TagConverter.getAsObject TagConverter.getAsObject ContactController.getAvailableTags() ContactController.getAvailableTags() ------AFTER PHASE PROCESS_VALIDATIONS 3 ------BEFORE PHASE UPDATE_MODEL_VALUES 4 Contact.setFirstName() ------AFTER PHASE UPDATE_MODEL_VALUES 4 ------BEFORE PHASE INVOKE_APPLICATION 5 ContactController.persist() ------AFTER PHASE INVOKE_APPLICATION 5 ------BEFORE PHASE RENDER_RESPONSE 6 ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() ContactController.getContacts() Contact.getFirstName() ContactController.getContacts() ContactController.getContacts() ------AFTER PHASE RENDER_RESPONSE 6 REQUEST END

JSF life cycle, conversion, validation, and phase listeners Page 44 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

Listing 47 shows going through all phases of the request life cycle. Notice that you get to the INVOKE_APPLICATION phase and the persist() method is called: ContactController.persist().

Adding an object-level validation framework

You could use a PhaseListener to capture phase-listener events and change the way JSF handles requests. Suppose you want to add your own object-level validation framework to JSF. You might use an interface like the one shown in Listing 48:

Listing 48. Validator interface

package com.arcmind.contact.model; public interface Validateable { public void validate() throws ValidationException; }

Then you change your Contact object to implement this interface, as shown in Listing 49:

Listing 49. Contact implementing validator interface

package com.arcmind.contact.model; import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.List; @SuppressWarnings("serial") public class Contact implements Serializable, Validateable { ... public void validate() throws ValidationException { if ( homePhoneNumber == null && workPhoneNumber == null && mobilePhoneNumber == null ){ throw new ValidationException("At least one phone number " + "must be set", ""); } } ...

You could create a PhaseListener that looks for Validateable objects and validates them. The PhaseListener could take the exception messages and convert them into FacesMessages, but how would you know when the object needs to be validated? It seems that knowledge would only be in the controller class. Thus, to handle this in a generic way, you could create a superclass controller that creates

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 45 of 52 developerWorks® ibm.com/developerWorks

an anonymous inner PhaseListener class that you could bind to, as shown in Listing 50:

Listing 50. Base class with PhaseListener

package com.arcmind.contact.controller; import java.io.Serializable; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import com.arcmind.contact.model.Validateable; import com.arcmind.contact.model.ValidationException; @SuppressWarnings("serial") public abstract class AbstractCrudController implements Serializable { private boolean edit; public PhaseListener phaseListener = new PhaseListener() { public void afterPhase(PhaseEvent event) { validate(); } public void beforePhase(PhaseEvent event) { } public PhaseId getPhaseId() { return PhaseId.UPDATE_MODEL_VALUES; } }; abstract Object getFormObject(); //subclass defines this private void validate() { Object form = getFormObject(); if (! (form instanceof Validateable) || form == null) { return; } Validateable validateable = (Validateable) form; try { validateable.validate(); //validate object } catch (ValidationException validationException) { FacesContext.getCurrentInstance().renderResponse(); //Do not invoke application. addErrorMessage(validationException.getMessage()); } } public PhaseListener getPhaseListener() { return phaseListener; }

protected void addErrorMessage(String message) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( FacesMessage.SEVERITY_ERROR, message, null)); }

JSF life cycle, conversion, validation, and phase listeners Page 46 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

public boolean isEdit() { return edit; } public void setEdit(boolean edit) { this.edit = edit; } }

Notice that the PhaseListener listens only to UPDATE_MODEL_VALUES. It then calls the validate() method, which checks to see if the form object is present and is an instance of Validator before it calls validate() on the domain object. It then calls validate() and allows the model to do the validation. Also notice that if the model validation fails, the validate() method of the AbstractCrudCOntroller calls FacesContext.getCurrentInstance().renderResponse(), which forces JSF to skip the INVOKE_APPLICATION phase.

Now you'd change ContactController to subclass AbstractCrudController, as shown in Listing 51:

Listing 51. ContactController extends AbstractCrudController to provide PhaseListener validation support

public class ContactController extends AbstractCrudController{ ... public void addNew() { ... same as before except sets the edit mode. super.setEdit(true); } public void persist() { ... same as before except sets the edit mode. super.setEdit(false); } public void read() { ... same as before except sets the edit mode. super.setEdit(true); } @Override Object getFormObject() { if (super.isEdit()) { return this.contact; } else { return null; } } }

Notice that Listing 51 implements the abstract getFormObject from the AbstractCrudController. It uses the edit mode so that it returns the contact object only when the controller is in edit mode. In this way, the controller decides

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 47 of 52 developerWorks® ibm.com/developerWorks

when the object should be validated.

Lastly, you need to register your phase listener, as shown in Listing 52:

Listing 52. Binding to the phaseListener property

Contacts (4th version)

With phase listeners, you can change how JSF handles requests, thereby extending the JSF framework to fit your needs.

Section 8. Conclusion

This tutorial covered JSF's request processing life cycle and demonstrated some of the essential features of its component model. It covered quite a bit when it comes to JSF conversion and validation. In fact, it has covered most of what you'll ever need to know about these processes to make them work in your applications (at least for this version of JSF)! Of course, this tutorial couldn't cover everything. For example, you might want to check out Tomahawk, Crank, Shale, and jsf-comp for validator components not offered in JSF or discussed here (see Resources). And although this tutorial discussed the most common conversion and validation techniques, there are others this tutorial didn't include.

Something to keep in mind is that conversion and validation don't necessarily work well together. Conversion converts strings into objects, whereas most of the standard validators work on strings. Therefore, you must execute caution when using custom converters and validators together. For instance, this tutorial's PhoneNumber object would not work with a length validator. In this case, you would either have to write a custom validator as well, or simply include any special validation logic in the custom converter. I prefer the latter option because it allows you simply to associate a custom converter (with built-in validation logic) with a specific object type and have JSF handle that object type. JSF does this for you automatically without making you specify any converter ids in the JSP. (Of course, some might call this lazy programming, and it isn't necessarily the best solution for all use cases.)

JSF provides a flexible, powerful, and pluggable framework for Web application development. In addition to standard converters and validators, JSF facilitates custom implementations to accommodate both application and framework

JSF life cycle, conversion, validation, and phase listeners Page 48 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

developers alike. Ultimately, the conversion and validation strategy you choose is up to you. JSF allows you to easily and quickly get started (standard converters, validators, inline validation) during prototyping, and migrate to more sophisticated production solutions (custom objects, custom messages) during later development phases. And throughout it all, the JSF application life cycle provides a reliable infrastructure for consistently ensuring data-model integrity.

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 49 of 52 developerWorks® ibm.com/developerWorks

Downloads

Description Name Size Download method Sample code for this article jsf12-part2-sample_code.zip239KB HTTP

Information about download methods

JSF life cycle, conversion, validation, and phase listeners Page 50 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved. ibm.com/developerWorks developerWorks®

Resources

Learn •"Getting started with JavaServer Faces 1.2, Part 1: Building basic applications" (developerWorks, December 2007): Get an overview JSF's features, and learn how to write a basic JSF application. Build and modify a simple calculator application. • Source code instructions: Find instructions on how to run this tutorial's source code with Eclipse JEE, and with Tomcat and Maven 2. • JSF Tag Library Documentation: Official documentation for the html and core tag libraries. • JSF API: API Javadocs for the JSF specification. •"Web Tier to Go With Java EE 5: Summary of New Features in JavaServer Faces 1.2 Technology": (Jennifer Ball and Ed Burns, java.sun.com, February 2006): Read about JSF 1.2's new features. •"Web Tier to Go With Java EE 5: Summary of New Features in JSP 2.1 Technology" (Pierre Delisle and Jennifer Ball, java.sun.com, February 2006): Get an overview of the new features introduced in version 2.1 of JSP technology. • java.text.SimpleDateFormat : Find a list of date-format patterns here. • Crank: Crank is a master/detail, CRUD, and annotation-driven validation framework built with JPA, JSF, Facelets, and Ajax. Crank is a use-case analysis of what is possible with the new JSF 2.0 stack. • JSF-comp: A component library for client-side validation. • Apache Shale: A JSF-based Web application framework. • Apache MyFaces Tomahawk: JSF-compatible components. • Facelets: Facelets is a view technology that focuses on building JSF component trees. •"Facelets fits JSF like a glove" (developerWorks, February 2006): In this article, get an introduction to Facelets' easy HTML-style templating and reusable composition components. •"Advanced Facelets programming" (Richard Hightower, developerWorks, May 2006): Read about advanced ways to bridge the gap between JSF and EL. • Browse the technology bookstore for books on these and other technical topics. • developerWorks Java technology zone: Hundreds of articles about every aspect of Java programming.

JSF life cycle, conversion, validation, and phase listeners © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 51 of 52 developerWorks® ibm.com/developerWorks

Get products and technologies • Build your next development project with IBM trial , available for download directly from developerWorks. Discuss • Check out developerWorks blogs and get involved in the developerWorks community.

About the author

Richard Hightower Rick Hightower serves as chief technology officer for ArcMind Inc.. He is coauthor of the popular book Java Tools for Extreme Programming and coauthor of Professional Struts and Struts Live . Rick is the founding developer on the Crank project, a JSF/Facelets, Ajax, CRUD framework for idiomatically developing GUIs.

Trademarks

Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.

JSF life cycle, conversion, validation, and phase listeners Page 52 of 52 © Copyright IBM Corporation 1994, 2008. All rights reserved.