JSF Nested Flows

27 July 2014
By Gonçalo Marques
In this article we will cover JSF nested flows: How to call a nested flow, pass parameters into the flow and return to the main flow.

Introduction

As we have seen in JSF Flow example, Java EE 7 brought the concept of flows into JSF. In this article we will not cover the basics (ex: flow configuration) as those subjects were already covered in the previous article. We will focus instead in nested flows creation: how to call a nested flow, how to pass parameters into a nested flow and how to return to the parent flow.

We will create a very simple main flow that creates an illustrative customer. After the customer creation, the user will be given the possibility of adding customer interests like technology or sports, but these will be added inside a nested flow.

This article considers the following environment:

  1. Ubuntu 12.04
  2. JDK 1.7.0.21
  3. Glassfish 4.0

The main flow

Since we will be creating customers in the main flow, we may start by defining the Customer class:

Customer.java
public class Customer {

  private Long id;
  private String name;
  private String address;
  private String phoneNumber;
  private List<String> customerInterests = new ArrayList<>();

  // Getters and setters

}

And the customer flow definition:

customer-flow.xml
<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="customer">
      
      <!-- Represents the call to the customer interests nested flow  -->
      <flow-call id="callInterestFlow">
            <flow-reference>
                <flow-id>interest</flow-id>
            </flow-reference>
            <outbound-parameter>
                <name>customerParam</name>
                <value>#{customerBean.customer}</value>
            </outbound-parameter>
        </flow-call>
        
      <!-- Represents the customer creation flow end (the main flow)  -->
    <flow-return id="returnFromCustomerFlow">
      <from-outcome>#{customerBean.returnValue}</from-outcome>
    </flow-return>
    
  </flow-definition>
  
</faces-config>

We are defining a flow-call element. This element defines a nested flow - trough the flow-reference element - that will be called from the main flow: the interest flow. We will also cover the interest flow definition in the next sections.

Inside the nested flow call definition we are also configuring an outbound parameter: customerParam. This parameter will be sent from the current flow into the nested flow. We may have as many outbound parameters as we need.

The customer flow main page definition follows next:

customer.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core">
<h:head>
  <title>Customer</title>
</h:head>
<h:body>
  <h:form>
    <h:outputText value="Create customer" />
    <br />
    <br />
    <h:outputLabel for="name" value="Name" />
    <h:inputText id="name" value="#{customerBean.customer.name}" />
    <br />
    <h:outputLabel for="address" value="Address" />
    <h:inputText id="address" value="#{customerBean.customer.address}" />
    <br />
    <h:outputLabel for="phone" value="Phone number" />
    <h:inputText id="phone" value="#{customerBean.customer.phoneNumber}" />
    <br />
    <br />
    <h:commandButton value="Save customer" action="#{customerBean.saveCustomer}" />
  </h:form>
</h:body>
</html>

Nothing special here. Just a customer creation form whose fields and action are bound to a @FlowScoped bean:

CustomerBean.java
@Named
@FlowScoped("customer")
public class CustomerBean {

  private Customer customer;

  @PostConstruct
  private void init() {
    customer = new Customer();
  }

  public String saveCustomer() {
    customer.setId(10L);
    return "customer-confirmation";
  }

  public String getReturnValue() {
    return "/home";
  }

  // Getters and setters

}

As we have discussed in the previous article which covered the JSF flow basics, saveCustomer() method is returning a String that matches the second step of the main flow (we will see it right next). The getReturnValue() method is used to define the outcome of the main flow termination action.

The second step of the customer flow creation is defined as follows:

customer-confirmation.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
  <title>Customer - Confirmation</title>
</h:head>
<h:body>
  <h:form>
    <h:outputText value="The customer has been created: #{customerBean.customer.id}" />
    <br />
    <h:outputText value="Name: #{customerBean.customer.name}" />
    <br />
    <br />
    <h:outputText value="Customer interests:" />
    <br />
    <ui:repeat var="interest" value="#{customerBean.customer.customerInterests}">
      <h:outputText value="#{interest}" />
      <br />
    </ui:repeat>
    <br />
    <br />
    <h:commandButton value="Add Interests" action="callInterestFlow" />
    <br />
    <h:commandButton value="Exit" action="returnFromCustomerFlow" />
  </h:form>
</h:body>
</html>

Now we have something new: The "Add Interests" command button action value is referencing the flow-call we defined previously in the customer flow XML configuration: callInterestFlow.

This means that when the user clicks the "Add Interests" command button, a new (nested) flow will be started, more precisely the interest flow, as we defined in the flow configuration file.

By looking at the configuration file we now know that we are passing the just created Customer instance into the nested flow by the means of the outbound-parameter XML configuration element. We will see in the next section how the nested flow receives this parameter and also how do we terminate the nested flow and return to the main flow.

The Customer interest list will be empty for now. Since we are passing the customer reference into the nested flow, as soon as we add interests to the list in that same nested flow and finally return to this page, the interest list contents will be shown.

The nested flow

The interest flow definition follows next:

interest-flow.xml
<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="interest">
      
      <!-- Represents the customer sent by the parent flow -->
      <inbound-parameter>
          <name>customerParam</name>
          <value>#{interestBean.customer}</value>
      </inbound-parameter>
      
      <!-- Represents the customer interests flow end and return to the main flow  -->
    <flow-return id="returnFromInterestFlow">
      <from-outcome>#{interestBean.returnValue}</from-outcome>
    </flow-return>
    
  </flow-definition>
  
</faces-config>

As we have said earlier, now we define the inbound parameter that will receive the customer passed by the main flow. Note that the parameter name matches the respective outbound parameter from the main flow: customerParam. We will bind it to a property of the InterestBean bean that we will see right next.

Another interesting configuration detail is the flow return: Since we are inside a nested flow, we will return to the parent flow upon nested flow termination. The target page may be any page in the parent flow, and will be defined by the returnValue property of the InterestBean bean.

The InterestBean bean is bound to the interest scope and is defined as follows:

InterestBean.java
@Named
@FlowScoped("interest")
public class InterestBean {

  private Customer customer;
  private String interest;

  public void addInterest() {
    customer.getCustomerInterests().add(interest);
    interest = "";
  }

  public String getReturnValue() {
    return "/customer/customer-confirmation";
  }

  // Getters and setters

}

And the corresponding interest flow page:

interest.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
  <title>Customer interests</title>
</h:head>
<h:body>
  <h:form>
    <h:outputText value="Add customer interests (customer id: #{interestBean.customer.id})" />
    <br />
    <br />
    <h:outputText value="Interest:" />
    <br />
    <h:inputText value="#{interestBean.interest}" />
    <br />
    <h:commandButton value="Add" action="#{interestBean.addInterest}">
      <f:ajax execute="@form" render="@form"/>
    </h:commandButton>
    <br />
    <br />
    <ui:repeat var="interest" value="#{interestBean.customer.customerInterests}">
      <h:outputText value="#{interest}" />
      <br />
    </ui:repeat>
    <br />
    <br />
    <h:commandButton value="End" action="returnFromInterestFlow" />
  </h:form>
</h:body>
</html>

In this form we add interests that would be associated with the current customer in a real scenario.

We are showing the customer ID from the Customer instance that we received from the main flow.

Since there will be a single bean instance for the entire interest flow duration we may effectively interact with the bean instance using Ajax requests (actually this is valid for any @FlowScoped bean: there will be a single flow scoped bean instance for any single flow instance).

Finally we terminate the nested flow and return to the parent flow by using the "End" command button. It is bound to the returnFromInterestFlow action, and if we review the interest flow XML configuration, we will see that the getReturnValue() method will be called to define the page from the parent flow that will be presented to the end user (it could be any page from the parent flow).

In our case it will be the customer creation confirmation view - /customer/customer-confirmation - and this time we will show the customer interest list contents that we just populated inside the nested flow.

The full working sample may be downloaded at the end of this page.

Reference

The Java EE 7 Tutorial:Using Faces Flows | Java EE Documentation

Download source code from this article

Download link: jsf-nested-flows.zip

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: http://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: