Wednesday, April 3, 2013

Communication in Spring Webflow 2

1   Introduction

This article tries to complement the reference documentation with examples on how to communicate within a flow in Spring Web flow 2, showing different ways to share data between controllers and views that form a flow. The article is divided into the following sections:
  • Setting flow variables
  • Setting attribute values
  • Using additional scopes
  • Communication with sub flows
  • Communication with other flows
  • Launching events with attributes
  • Accessing RequestContext
  • Accessing Spring beans
  • Using implicit variables

The example explained here uses Spring Web flow 2.3.1 and Spring 3.0.5 versions. You can get the source code here, with all the examples shown in this article.


2   Setting flow variables

There are different ways of setting variables and storing them into the flow scope, where they will then be accessible to controllers and views.

2.1   var element (variable)

The var element instantiates a class and stores it in the flow scope. Since the instance will be stored between flow requests, the class should implement java.io.Serializable. It does not allow you to assign a value, making this element useless for classes without a default constructor (it will crash at runtime with a nice NoSuchMethodException). In that case, you can use the set element (see section 3).

Defining a variable:

You can use this variable in the flow definition, for example, passing it as a parameter to a controller:

The method will receive the car parameter:
Since it is automatically stored in the flow scope, you can also retrieve the value from the request context:
Or use it in the view using for example ${car.color}

2.2   At flow starting point

If your class has no default constructor or you want to retrieve the instance from a service, the element var won't be enough. You can use a controller instead:
In this sample, the information could be retrieved from a service. The result attribute will get the object returned by the controller and store it in the flow scope.

You can also use this approach when entering a state (on-entry) or before rendering a view (on-render).

2.3   Anywhere in the flow

Since you have access to the Web flow RequestContext when invoking a controller, you can set a flow scoped variable as shown below:


3   Setting attribute values

The set element allows you to define an attribute and specify a scope. This element takes the following attributes:
  • name: Scope and name for the attribute, delimited by a dot (scope.name).
  • value: Value set to the attribute.
  • type: Class type.

For example, you can use it at the beginning of a flow (on-start) or when entering into a state (on-entry). The following sample show how to assign it a specified value when the flow starts:
Or you could set it when launching a transition:

The set element not only allows you to define objects, but also String values. For example, if you have a bean named 'myBean', the first set element in the following sample, will retrieve the Car instance from the bean and store it in the request scope with the name 'carObject'. On the other hand, the second set element will store in the request scope an attribute named 'carString' that will contain the String 'myBean.car'.

You can also use implicit variables when setting the value of the set element (see section 10 for a list of these variables):


4 Using additional scopes

The RequestContext interface contains access to all the other scopes defined in Spring Web flow: request, flash, view, flow and conversation.

You can also access these scopes at flow definition level by using implicit EL variables, which are: requestScope, flashScope, viewScope, flowScope and conversationScope.

For other scopes, you can use the external context:

At flow definition using implicit variables:

Or at controller level through RequestContext interface:


5   Communication with sub flows

When invoking a sub flow from the main flow, you can pass it input attributes. Once the sub flow has finished, it may return output attributes.

Input:
The input element allows you to send parameters to the sub flow. When the sub flow starts, these input attributes are stored in the flow scope of the sub flow. You will need to define the input element in the sub flow, using the same name attribute as used in the main flow.

Output:
Once the sub flow ends, the main flow can receive output parameters. These output parameters are defined within sub flow's end-states. When the execution returns to the main flow. output parameters will be available as attributes inside the launched event.

Main flow: invoking a sub flow

When returning to the main flow, you will need to define an attribute with the value returned by the sub flow in order to make it accessible to following states.

You could also invoke a controller that would set the value and store it in the needed scope:

Sub flow definition

There are other options to pass information to a subflow which consists in the following:
  • Storing the information at conversation scope (this scope is available from within a flow and its sub flows).
  • Pass the information in the transition which access the sub flow:


6   Communication with other flows

You have two options of passing data to another flow which is not related to the current flow:
  1. Session scoped attributes
  2. URL Request parameters
If you choose the second option, you can do it the following way:

In the view:

When starting the other flow, you can use the requestParameters implicit variable to retrieve the value and store it in the needed scope.

You could also do it in the controller:

Or use the requestParameterMap directly through the getRequestParameters shortcut method:


7   Launching events with attributes

When exiting a controller, it is possible to add attributes to the current event. To do that you need to generate an attribute map.

The controller which launches the event adds the attribute as follows:


8   Accessing the request context

If you want to invoke methods from classes other than controllers, like beans, it is possible to retrieve the web flow request context. There are two ways:

Passing the request context as a parameter to the bean method:

Or you can use the RequestContextHolder class:


9   Accessing Spring beans

If you need to retrieve beans from the Spring context, you can implement a utility class. The following method accesses the Spring context through the web flow request context.


10   Using implicit variables

There's a full list of all available implicit variables at the web flow reference documentation

springsource web flow reference (el-variables)


8 comments:

  1. Perfecto!!! It saved couple of hours

    ReplyDelete
  2. Good info.Keep it up

    ReplyDelete
  3. Great article. Thanks a lot.

    ReplyDelete
  4. Excellent tuto Xavier! But I got a doubt...could you say me why you need writing a function to get beans? (Section 9) Because I had understood that I can get any spring bean only referencing its name at somewhere from a flow or using @inject/autowired on code (also jsf...I think). Thanks!

    ReplyDelete
    Replies
    1. Hello and thanks Rafael!

      You could autowire singleton or prototype scoped beans in the controller using @Autowired. However, if you try to autowire a flow scoped bean you will get an illegal exception, since when Spring resolves it, no flow execution is running yet.

      Delete