ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Page Navigation in JavaServer Faces

by Budi Kurniawan
10/29/2003

Page navigation is an important aspect of web programming. The more complex an application is, the harder it is to manage how users navigate from one page to another. JavaServer Faces (JSF) solves this problem by allowing page navigation to be configured in the Application Configuration file. This is one of the features that will make JSF the first choice of web application development in Java in the future. You can download the sample application here. The examples were tested using JSF Early Access 4. Refer to the article "Introducing JavaServer Faces" on ONJava.com on how to deploy your JSF application. The article can also be the first reference if you are not familiar with JSF.

All multi-page web applications need some mechanism to manage page navigation for their users. If you are familiar with Model 1 and Model 2 of the servlet and JSP design models, you know that page navigation can be complicated in Model 1 applications because the link to the user's next destination is hard-coded. The difficulty of page navigation management often makes Model 1 inappropriate for medium-sized-to-large applications. Model 2 alleviates the problem somewhat, because the next links are confined to the Controller servlet. However, changing a link requires the recompilation of the servlet. JSF, however, offers page navigation through page navigation rules in the Application Configuration file.

Page navigation is crucial to JSF application programming. All multi-page JSF applications will require the user to navigate between pages. In JSF, the user navigates to another page by clicking a UICommand component on the page, represented by either the command_button tag or the command_hyperlink tag. Clicking this component emits an Event object of the type javax.faces.event.ActionEvent. Normally, to capture an ActionEvent, you need to write an ActionListener. However, JSF provides a default action listener for page navigation. As a result, you don't have to write your own. But how does the default action listener know which page to present next when the user clicks a UICommand? Through the page navigation rules in the Application Configuration file. Because the Application Configuration file is an XML document, you can use any text editor to update it. This means page navigation is easy to modify.

Related Reading

Java Servlet & JSP Cookbook
By Bruce W. Perry

This article explains what navigation rules are and how to use them. It continues with an example of a simple navigation. Afterwards, it discusses the Action class and its role in page navigation. The article concludes with another more complex example that illustrates conditional page navigation. Let's start with navigation rules.

Navigation Rules

Clicking the UICommand component will send an ActionEvent to the default application handler. The default application handler is registered automatically to every UICommand component.

Each navigation rule in the Application Configuration file specifies a page of origin and one or more possible targets. A page is represented by its tree identifier, and each possible target is represented by a navigation case. You need to specify a navigation rule for each page from which the user can navigate to another page.

To understand this better, consider a page called login.jsp that contains a login form. When the user clicks the submit button to log in, the application can redirect control to either a welcome.jsp page (if the login was successful) or back to the login.jsp page (if the login failed). In this case, the login.jsp page will have two navigation cases, one for a successful login and one for a failed login.

A navigation rule is defined by the navigation-rule element in the Application Configuration file. The element is described as follows.

<!ELEMENT navigation-rule
    (description*, display-name*, icon*, from-tree-id?, navigation-case*)>

The asterisk denotes that the element preceding the asterisk appears zero or many times. Therefore, description, display-name, icon, and navigation-case are all optional or can appear many times. The question mark (?) denotes that the preceding element can appear zero or one time. The two most important sub-elements of the navigation-rule element are from-tree-id and navigation-case.

The value of a from-tree-id sub-element is the tree identifier of the page of origin. To describe the navigation rule for a JSP page called login.jsp, you would have the following as the value of the from-tree-id sub-element:

<from-tree-id>login.jsp</from-tree-id>

The navigation-case sub-element represents a possible target. A navigation-rule element can have zero or more navigation-case sub-elements. Each of these elements specifies the target page for a particular outcome of the from-tree-id processing. An outcome can be obtained from the action or actionRef attributes of the UICommand component in the from-tree-id element.

The navigation-case element is described as follows:

<!ELEMENT navigation-case
    (description*, display-name*, icon*, from-action-ref?, from-outcome?, to-tree-id)>

Use the description element to describe the navigation case. The display-name and icon sub-elements are useful only for an XML tool you use to edit the Application Configuration file. Both are infrequently used. The from-action-ref, from-outcome, and to-tree-id are explained below.

The to-tree-id element specifies the target page of this navigation case. The from-outcome is the outcome of processing the from-tree-id. It is obtained from the value of the action property of the UICommand component that triggered the ActionEvent in the from-tree-id.

The from-action-ref element also represents the outcome of processing the from-tree-id. However, its value comes from the actionRef property of the UICommand component that raised the ActionEvent.

Simple Example

The following example illustrates the simplest navigation rule. This application consists of two pages: a.jsp and b.jsp. From a.jsp, the user can click the button to go to b.jsp, and vice versa. Listing 1 shows the navigation rules.

Listing 1. The navigation rules for a.jsp and b.jsp

<navigation-rule>
    <from-tree-id>/a.jsp</from-tree-id>

    <navigation-case>
        <to-tree-id>/b.jsp</to-tree-id>
    </navigation-case>
</navigation-rule>

<navigation-rule>
    <from-tree-id>/b.jsp</from-tree-id>

    <navigation-case>
        <to-tree-id>/a.jsp</to-tree-id>
    </navigation-case>
</navigation-rule>

The first navigation-rule element specifies the target for a.jsp. It contains the navigation-case element with a to-tree-id sub-element, without a from-outcome or a from-action-ref sub-element. This means that, regardless of the outcome, the target page (b.jsp) will be displayed. The second navigation rule specifies the target for b.jsp. From b.jsp, the user will definitely go to a.jsp.

Note: the original page will be re-displayed if there is an error processing it.

Both a.jsp and b.jsp use the ValueBean in Listing 2.

Listing 2. The ValueBean class

package jsfArticle;

public class ValueBean {
    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

The bean must be registered in the Application Configuration file given in Listing 3.

Listing 3. ValueBean in the Application Configuration file

<managed-bean>
    <managed-bean-name>ValueBean</managed-bean-name>

    <managed-bean-class>jsfArticle.ValueBean</managed-bean-class>

    <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

The a.jsp contains a UIInput component and a validator that enforce the user to type four or more characters into the UIInput component. The a.jsp page is given in Listing 4.

Listing 4. a.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>First Page</title>
</head>
<body>
<f:use_faces>
<h:form  formName="myForm">
Please enter your user id (Your user id must be at least 4
characters long) :
  <h:input_text valueRef="ValueBean.value">
    <f:validate_length minimum="4"/>
  </h:input_text>
<br/>
<h:command_button commandName="submit" label="submit"/>
<br/>
<hr/>
<h:output_errors/>
</h:form>
</f:use_faces>
</body>
</html>

The b.jsp page contains a UIOutput component that displays the value entered by the user to the UIInput component in the a.jsp page. The b.jsp page also has a command button for the user to go back to the a.jsp page. The b.jsp page is given in Listing 5.

Listing 5. b.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>Second Page</title>
</head>
<body>
<f:use_faces>
<h:form  formName="myForm">
The user id you have entered is <h:output_text
valueRef="ValueBean.value"/>
<br/>
<h:command_button commandName="back"
	label="Back to Previous Page"/>
</h:form>
</f:use_faces>
</body>
</html>

You can invoke the a.jsp page using the following URL (assuming your web container is using port 8080, and that you are running the browser on the same machine that hosts the applications):

http://localhost:8080/jsfPageNav/faces/a.jsp

You will then see something similar to Figure 1.

The a.jsp page
Figure 1. The a.jsp page

Now, type and submit a string of four or more characters. Your browser will now look like the one in Figure 2.

The b.jsp page
Figure 2. The b.jsp page

If you click the Back button in b.jsp, you will see the a.jsp page again.

Note that if you type fewer than four characters into the UIInput component in a.jsp page, the validator in a.jsp will add a Message object to the FacesContext instance, canceling the navigation. As a result, you will see the a.jsp page again, with an error message, just like the screenshot in Figure 3.

Failed navigation
Figure 3. Failed navigation

The Action Class

An Action is an object that performs a task. In JSF, an Action is represented by the abstract javax.faces.application.Action class.

The most important method here is invoke method, an abstract method that returns a String. Its signature is as follows:

public abstract String invoke()

You write the code to perform the task for this Action object in this method. The JSF implementation will call your invoke method.

An Action can be invoked by a UICommand via the actionRef attribute of its command_button or command_hyperlink tags. For an instruction on how to use this class, see the example in the section "Conditional Page Navigation."

Simple Navigation

The following example illustrates the simplest navigation rule. This application consists of two pages: page1.jsp and page2.jsp. From page1.jsp, the user can click the button to go to page2.jsp, and vice versa. The navigation rules in Listing 1 specify the navigation from both page1.jsp and page2.jsp.

<navigation-rule>
    <from-tree-id>/page1.jsp</from-tree-id>

    <navigation-case>
        <to-tree-id>/page2.jsp</to-tree-id>
    </navigation-case>

</navigation-rule>

<navigation-rule>
    <from-tree-id>/page2.jsp</from-tree-id>

    <navigation-case>
        <to-tree-id>/page1.jsp</to-tree-id>
    </navigation-case>

</navigation-rule>

The first navigation-rule element specifies the target for page1.jsp. It contains the navigation-case element with a to-tree-id sub-element, without a from-outcome or a from-action-ref sub-element. This means that, regardless of the outcome, target page (page2.jsp) will be displayed. The second navigation rule specifies the target for page2.jsp. From page2.jsp, the user will definitely go to page1.jsp.

Note: the original page will be re-displayed if there is an error in processing.

Both page1.jsp and page2.jsp use the TestingBean. This bean is registered in the Application Configuration file as follows:

<managed-bean>
    <managed-bean-name>TestingBean</managed-bean-name>

    <managed-bean-class>ch06.TestingBean</managed-bean-class>

    <managed-bean-scope>session</managed-bean-scope>

</managed-bean>

page1.jsp contains a UIInput component and a validator that forces the user to type four or more characters into the UIInput component. The page1.jsp page is given in Listing 6.

Listing 6. page1.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>Page 1</title>
</head>
<body>
<f:use_faces>
<h:form  formName="myForm">
Enter your user name (minimum 4 characters) :
  <h:input_text valueRef="TestingBean.value">
    <f:validate_length minimum="4"/>
  </h:input_text>
<br>
<h:command_button commandName="submit" label="submit"/>
<br>
<h:output_errors/>
</h:form>
</f:use_faces>
</body>
</html>

page2.jsp contains a UIOutput component that displays the value entered by the user to the UIInput component in the page1.jsp page. The page2.jsp page also has a command button for the user to go back to the page1.jsp page. The page2.jsp page is given in Listing 7.

Listing 7. page2.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>Page 2</title>
</head>
<body>
<f:use_faces>
<h:form  formName="myForm">
Your user name is <h:output_text
valueRef="TestingBean.value"/>
<br>
<h:command_button commandName="back" label="Back"/>
</h:form>
</f:use_faces>
</body>
</html>

You can invoke the page1.jsp page using the following URL:

http://localhost:8080/JSFCh06/faces/page1.jsp

Now, type in a string of four or more characters. You should see page2.jsp welcoming you. If you click the Back button, you will see the page1.jsp page again.

Note that if you type less than four characters into the UIInput component in page1.jsp page, the validator in page1.jsp will add a Message object to the FacesContext instance, and this will cause the navigation to be cancelled. As a result, you will see the page1.jsp page again, with an error message.

Conditional Page Navigation

This section presents an example that demonstrates how to use a navigation rule that has more than one navigation case. The example contains two JSP pages: login.jsp and welcome.jsp. The login.jsp page contains a login form with two UIInput components for the user name and the password. To log in, the user clicks the command button. Upon a successful login, the user will be directed to the welcome.jsp page. If the login failed, the user will see the login.jsp page again, this time with an error message.

The navigation rule for this example is given in Listing 8.

Listing 8. The navigation rule for conditional page navigation

<navigation-rule>
    <from-tree-id>/login.jsp</from-tree-id>

    <navigation-case>
        <from-outcome>success</from-outcome>

        <to-tree-id>/welcome.jsp</to-tree-id>

    </navigation-case>

    <navigation-case>
    <from-outcome>failed</from-outcome>

    <to-tree-id>/login.jsp</to-tree-id>

    </navigation-case>
</navigation-rule>

The navigation-rule element specifies the possible targets for the login.jsp page. For the from-outcome element value of success (a successful login), the welcome.jsp page will be displayed. Otherwise, if the from-outcome element value is failed, the login.jsp page is re-displayed. There is no navigation rule for welcome.jsp, but you can add one yourself.

The login.jsp and welcome.jsp pages are given in Listings 9 and 10 respectively.

Listing 9. login.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<f:use_faces>
<h:form  formName="loginForm">
<h2>Please enter your user name and password</h2>
<br>User Name: <h:input_text
valueRef="LoginBean.userName"/>
<br>Password: <h:input_secret
valueRef="LoginBean.password"/>
<br><h:command_button commandName="login"
label="login"
actionRef="LoginBean.login"/>
<br>
<h:output_errors/>
</h:form>
</f:use_faces>
</body>
</html>

Listing 10. welcome.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Welcome</title>
</head>
<body>
You logged in successfully.
</body>
</html>

The command_button tag in the login.jsp page has the actionRef attribute with the value of LoginBean.login:

<h:command_button commandName="login" label="login"
	actionRef="LoginBean.login"/>

This means that the outcome of the command button will come from the login property of the LoginBean. The LoginBean is listed in Listing 11. Pay special attention to the LoginAction class, which will be explained after the code listing.

Listing 11. LoginBean

package jsfArticle;

import javax.faces.application.Action;
import javax.faces.application.Message;
import javax.faces.application.MessageImpl;
import javax.faces.context.FacesContext;

public class LoginBean {
    private String userName;
    private String password;
    private Action login;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public Action getLogin() {
    if (login==null)
            login = new LoginAction();
        return login;
    }

    class LoginAction extends Action {
        public String invoke() {
            if ("anna".equals(userName) && "honolulu".equals(password))
                return "success";
            else {
                Message loginErrorMessage =
                    new MessageImpl(1, "<hr>Login failed", null);
                FacesContext.getCurrentInstance().addMessage(
                    null, loginErrorMessage);
                return "failed";
            }
        }
    }
}

package ch06;

import javax.faces.application.Action;
import javax.faces.application.Message;
import javax.faces.application.MessageImpl;
import javax.faces.context.FacesContext;

public class LoginBean {
    private String userName;
    private String password;
    private Action login;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) { 
        this.password = password;
    }

    public Action getLogin() {
        if (login==null)
            login = new LoginAction();
        return login;
    }

    class LoginAction extends Action {
        public String invoke() {
            if ("ken".equals(userName) && "blackcomb".equals(password))
                return "success";
            else {
                Message loginErrorMessage =
            new MessageImpl(1, "<hr>Login failed", null);
                FacesContext.getCurrentInstance().addMessage(
            null, loginErrorMessage);
                return "failed";
            }
        }
    }
}

For a bean's property to represent an action's outcome, the property must be of type javax.faces.application.Action. The login property has this type:

private Action login;

The getLogin method returns the login Action.

public Action getLogin() {
    if (login==null)
        login = new LoginAction();
    return login;
}

The implementation of the Action class must provide implementation of the invoke method. According to the navigation rule in Listing 8, the value must be either success or failed. These are the possible return values of the invoke method in the LoginAction class. In this case, the correct user name and password are ken anna and blackcombhonolulu, respectively.

The example uses the LoginBean, which is registered in the Application Configuration file using the managed-bean element in Listing 12.

Listing 12. Registering the LoginBean

<managed-bean>
    <managed-bean-name>LoginBean</managed-bean-name>

    <managed-bean-class>jsfArticle.LoginBean</managed-bean-class>

    <managed-bean-scope>session</managed-bean-scope>

  </managed-bean>

You can invoke the login.jsp page using the following URL:

http://localhost:8080/JSFCh0jsfPageNav6/faces/login.jsp

You'll see something similar to Figure 4 in your browser.

The login.jsp page
Figure 4. The login.jsp page

If you type in the correct user name and password, you'll see the welcome.jsp page, as in Figure 5. Otherwise, the login.jsp page will be re-displayed, with an error message, as in Figure 6.

The welcome.jsp page
Figure 5. The welcome.jsp page upon a successful login

A failed login
Figure 6. A failed login

Note that in this example and the previous one, you may use the from-action-ref element, as in Listing 13.

Listing 13. The navigation rule with from-action-ref elements

<navigation-rule>
<from-tree-id>/login.jsp</from-tree-id>

    <navigation-case>
        <from-action-ref>LoginBean.login</from-action-ref>

        <from-outcome>success</from-outcome>

        <to-tree-id>/welcome.jsp</to-tree-id>

    </navigation-case>

    <navigation-case>
        <from-action-ref>LoginBean.login</from-action-ref>

        <from-outcome>failed</from-outcome>

        <to-tree-id>/login.jsp</to-tree-id>

    </navigation-case>
</navigation-rule>

However, this is not necessary, because the possible action values from the bean are all unique.

Using the from-action-ref Element

This example demonstrates that you can get the outcome either from an action-ref attribute or an action attribute. This example uses a login2.jsp, which is similar to login.jsp. However, login2.jsp contains a hyperlink that goes to the help.jsp page when clicked.

Warning: You need to restart your browser to force JSF to create a new instance of the LoginBean.

Listing 14 shows the navigation-rule element in the Application Configuration file.

Listing 14. The navigation rule

<navigation-rule>
    <from-tree-id>/login2.jsp</from-tree-id>

    <navigation-case>
        <from-outcome>success</from-outcome>

        <to-tree-id>/welcome.jsp</to-tree-id>

    </navigation-case>

    <navigation-case>
        <from-outcome>failed</from-outcome>

        <to-tree-id>/login2.jsp</to-tree-id>

    </navigation-case>

    <navigation-case>
        <from-outcome>help</from-outcome>

        <to-tree-id>/help.jsp</to-tree-id>

    </navigation-case>
</navigation-rule>

The login2.jsp page is given in Listing 15, and help.jsp in Listing 16.

Listing 15. login2.jsp

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<html>
<head>
<title>Login 2</title>
</head>
<body>
<f:use_faces>
<h:form formName="loginForm">
<h2>Please enter your user name and password</h2>
<br>User Name: <h:input_text
valueRef="LoginBean.userName"/>
<br>Password: <h:input_secret
valueRef="LoginBean.password"/>
<br><h:command_button commandName="login"
label="login"
actionRef="LoginBean.login"/>
<br><h:command_hyperlink commandName="forgottenPassword"
action="help"
label="Forgotten your password?"/>
<h:output_errors/>
</h:form>
</f:use_faces>
</body>
</html>

Listing 16. help.jsp

<html>
<head>
<title>Help</title>
</head>
<body>
If you have forgotten your password, please contact the admin
(admin@brainysoftware.com).
</body>
</html>

Now you can invoke login2.jsp using this URL:

http://localhost:8080/JSFCh0jsfPageNav6/faces/login2.jsp

Your browser will display something similar to Figure 7.

The login2.jsp page
Figure 7. The login2.jsp page

If you enter the correct user name and password, you'll see the welcome.jsp page. Typing in the wrong user name and password returns you to the login2.jsp page. If you click the hyperlink, you'll see the help.jsp page in Figure 8.

A failed login
Figure 8. The help.jsp page

However, suppose you use success as the value of the action attribute in the command_hyperlink tag in the page2.jsp page:

<h:command_hyperlink commandName="forgottenPassword"
    action="success" label="Forgotten your password?"/ >

The navigation-rule element would have to be written as follows (this time including the from-action-ref element):

<navigation-rule>
    <from-tree-id>/login2.jsp</from-tree-id>

    <navigation-case>
        <from-action-ref>LoginBean.login</from-action-ref>

        <from-outcome>success</from-outcome>

        <to-tree-id>/welcome.jsp</to-tree-id>

    </navigation-case>

    <navigation-case>
        <from-action-ref>LoginBean.login</from-action-ref>

        <from-outcome>failed</from-outcome>

        <to-tree-id>/login2.jsp</to-tree-id>

    </navigation-case>

    <navigation-case>
        <from-outcome>success</from-outcome>

        <to-tree-id>/help.jsp</to-tree-id>

    </navigation-case>
</navigation-rule>

Conclusion

This article has demonstrated page navigation, a very important aspect of JSF application programming. This article first presented the navigation rules and continued with several examples.

Budi Kurniawan is a senior J2EE architect and author.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.