Faces Flow tutorial

This tutorial discusses about Faces Flow which is an addition to Java Server Faces 2.2, included in the Java EE 7 stack. Faces Flow has been inspired by the popular Spring Flow framework and as such it is not intended to be a replacement for the Web application navigation system; rather Faces Flow can be used to encapsulate a set of steps guiding the user through the execution of a business tasks.  

We will start checking a basic example of an User Registration using a Faces Flow, next we will see how we can combine together two Flows.
The first flow that we will create is called "signup". Faces flows mandates that you create a folder with the flow's name into the WebApp directory. Within this folder you can include the flow configuration file and the pages that are part of the flow.

Please note that it is not mandatory to use a configuration file to define the flow that can also be coded programmatically. However if you are using a configuration file, this file must be named as flowName-flow.xml.

Here is a snapshot of our Web app folder which contains the three pages that make up the User Signup wizard:

faces flow tutorial

This is the signup-flow.xml which defines our signup flow:

<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    
    <flow-definition id="signup">
        <flow-return id="homePage">
            <from-outcome>#{signupBean.homeAction}</from-outcome>
        </flow-return>
    </flow-definition>
    
</faces-config>

Here, the id attribute of the flow-definition element defines the name of the flow as signup. The value of the id attribute of the flow-return element identifies the name of the return node, and its value is defined in the from-outcome element as the homeAction property of the flow-scoped managed bean for the signup flow, SignupBean.

Let's see in detail the pages that make up the flow. Here is signup.xhtml that is meant to be the first page in the flow:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">

    <head>
        <title>Signup account</title>
    </head>
    <body>
        <h:form prependId="false">
            <h1>Signup Account</h1>
             <p>Name <h:inputText id="name" value="#{flowScope.name}" /></p>
             <p>Surname: <h:inputText id="surname" value="#{flowScope.surname}" /></p>
             <p>Email: <h:inputText id="email" value="#{flowScope.email}" /></p>      
   
            <p><h:commandButton id="page2" value="next" action="signup2" /></p>

        </h:form>
    </body>
</html>

Here pay attention to the flowScope attributes which are special kind of attributes used to transport data across the flow. The command button named "page2" transport the navigation to the signup2.xhtml page which follows here:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">

    <head>
        <title>Confirm data and Accept</title>
    </head>
    <body>
        <h:form prependId="false">
            <h1>Confirm data and Accept</h1>

                <p>Name: #{flowScope.name}</p>
                <p>Surname: #{flowScope.surname}</p>
                <p>Surname: #{flowScope.email}</p>
                
                <p>Please Accept License Agreement and click Next</p>
                <p>Accept : <h:selectBooleanCheckbox id="input" value="#{signupBean.licenseAccepted}" /></p>
                <p><h:commandButton id="next" value="next" action="#{signupBean.accept}" /></p>
        
                <p><h:commandButton id="back" value="back" action="signup" /></p>
             
            <h:messages globalOnly="true" showDetail="false" showSummary="true"/>
        </h:form>
    </body>
</html>

As you can see, we are asking to confirm the attributes inserted in the flowScope and we ask to user to confirm some license agreement. This is done in combination with the SignupBean which contains a boolean value for "licenseAccept" and an action for the method "accept". Here is the Signup Bean:

@Named
@FlowScoped("signup")
public class SignupBean implements Serializable {

    private boolean licenseAccepted;
 public SignupBean() {
        
    }

    public String getHomeAction() {
        return "/index";
    }
    public boolean isLicenseAccepted() {
        return licenseAccepted;
    }

    public void setLicenseAccepted(boolean licenseAccepted) {
        this.licenseAccepted = licenseAccepted;
    }
    public String accept() {
        
        if (this.licenseAccepted) {
            return "signup3";
        } else {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "You have to read and accept the license!", "You have to read and accept the license!"));
            return null;
        }
    }
}

The most interesting part is @FlowScoped which is a CDI scope that binds the scope of bean in the specified flow. This enables automatic activation/passivation of the bean when the scope is entered/exited.
In our example, to pass through the signup2 screen, we need to click on accept which returns "signup3" that is the last page in the flow:
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://java.sun.com/jsf/html">

    <head>
        <title>Last page in the flow</title>
    </head>
    <body>

        <h1>Registration completed!</h1>
         <p>value: #{flowScope.name}</p>
         <p>value: #{flowScope.surname}</p>

        <h:form prependId="false">
            <p><h:commandButton id="back" value="back" action="signup2" /></p>
            <p><h:commandButton id="home" value="home" action="homePage" /></p>

        </h:form>
    </body>
</html>

As side note, consider that you can define your flow schema also by means of CDI Producers, by annotating your CDI Producer with the @FlowDefinition annotation and constructing the navigation path using the FlowBuilder object as follows:
public class Signup implements Serializable {
    
    @Produces
    @FlowDefinition
    public Flow defineFlow(@FlowBuilderParameter FlowBuilder flowBuilder) {
        String flowId = "signup";
        flowBuilder.id("", flowId);
        flowBuilder.viewNode(flowId, "/" + flowId + "/" + flowId + ".xhtml").markAsStartNode();
        
        flowBuilder.returnNode("homePage")
                   .fromOutcome("#{signupBean.homeAction}");
        
        return flowBuilder.getFlow();
    }
}

In the second example of FacesFlow we will mixup two flows: the first one is still the signup flow; we will add another flow named "confirm" that is meant to be used to enter a confirmation id that has been sent by email.
Here's a snapshot of the WebApp:

faces flow tutorial

The most relevant changes to this project is the navigation flow configuration file which now contains two a new element: a flow-call which reference the "confirm" flow via the id "callsettingsFlow". This will be used to navigate between the two flows. Next we pass one flow parameter, the email collected in the signup flow, assigning it a logical name "paramFromSignup":

<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">

 <flow-definition id="signup">
  <flow-return id="homePage">
   <from-outcome>#{signupBean.homeAction}</from-outcome>
  </flow-return>

  <flow-call id="callSettingsFlow">
   <flow-reference>
    <flow-id>confirm</flow-id>
   </flow-reference>

   <outbound-parameter>
    <name>paramFromSignup</name>
    <value>#{flowScope.email}</value>
   </outbound-parameter>

  </flow-call>
 </flow-definition>

</faces-config>

The only change to the signup flow is contained in signup3.xhtml, which now has an extra button that triggers the action "callSettingsFlow" as defined in the flow-call:
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:h="http://java.sun.com/jsf/html">

    <head>
        <title>Last page in the flow</title>
    </head>
    <body>

        <h1>Registration completed!</h1>
         <p>Name: #{flowScope.name}</p>
         <p>Surname: #{flowScope.surname}</p>

        <h:form prependId="false">
            <p><h:commandButton id="back" value="back" action="signup2" /></p>
            <p><h:commandButton id="home" value="home" action="homePage" /></p>
            <p><h:commandButton id="callflow" value="Enter Flow 2" action="callSettingsFlow" /></p>

        </h:form>
    </body>
</html>

The confirm flow configuration file contains the following flow definition:
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
    
    <flow-definition id="confirm">
        <flow-return id="homePage">
            <from-outcome>#{confirmBean.homeAction}</from-outcome>
        </flow-return>
        
        <inbound-parameter>
            <name>paramFromSignup</name>
            <value>#{flowScope.param1Value}</value>
        </inbound-parameter>
      
    </flow-definition>
    
</faces-config>

Here the relevant part is the inbound-parameter section is used to receive the parameter from the signup flow. The name and value is a bit confusing at first, however the name "paramFromSignup" is used to bind the parameter name between the configuration files. Its value is used to assign it to a scope variable that will be used in the flow.
As you can see from the confirm.xhtml page, we are referencing the parameter through the flowScope.param1Value. This is the email parameter coming from the signup form.
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">

    <head>
        <title>Confirm Email</title>
    </head>
    <body>
        <h:form prependId="false">
            <h1>Confirm Email</h1>
                   <p>An email with a secret key has been sent to: #{flowScope.param1Value}</p>
                   
                      <p>Enter key <h:inputText id="key" value="#{confirmBean.key}" /></p>
                    
                      <p><h:commandButton id="next" value="next" action="#{confirmBean.checkKey}" /></p>
           </h:form>
    </body>
</html>

This page requires that the user enters a key to confirm the registration. To keep it simple the key has been printed out on the console by the ConfirmBean, however logically it's similar to a standard User registration process.
@Named
@FlowScoped("confirm")
public class ConfirmBean implements Serializable {
    @PostConstruct
    public void createKey() {
     secretKey = UUID.randomUUID().toString();
     System.out.println("Secret Key is "+secretKey);
    }
 public ConfirmBean() {
        
    }
    private String key;
    private String secretKey;
    
    public String getHomeAction() {
        return "/index";
    }
    public String checkKey() {
        
        if (key.equals(secretKey)) {
            return "confirm2";
        } else {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Wrong key!", "You have entered a wrong key!"));
            return null;
        }
    }
    public String getSecretKey() {
  return secretKey;
 }

 public void setSecretKey(String secretKey) {
  this.secretKey = secretKey;
 }

 public String getKey() {
  return key;
 }

 public void setKey(String key) {
  this.key = key;
 } 
}

If the user enters correctly the key, the checkKey() method completes the navigation to the screen named "confirm2" which simply prints out a success message, completing the flow:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">

    <head>
        <title>Confirm Email</title>
    </head>
    <body>
        <h:form prependId="false">
            <h1>Congratulations you have completed the Registration</h1>
                           
           </h:form>
    </body>
</html>

wildfly development bookYou can download here the project from here which contains the BOMs and dependencies to be used for deploying the application on WildFly 8.

This article is an excerpt from the new coming book, Practical Java EE 7 development using WildFly.

 

Follow us on Twitter