12. Expression Language (EL)
Web Flow uses EL to access its data model and to invoke actions. This chapter should familiarize you with EL syntax, configuration, and special EL variables you can reference from your flow definition.
EL is used for many things within a flow, including:
-
Accessing client data, such as declaring flow inputs or referencing request parameters.
-
Accessing data in Web Flow’s
RequestContext
, such asflowScope
orcurrentEvent
. -
Invoking methods on Spring-managed objects through actions.
-
Resolving expressions, such as state transition criteria, sub-flow IDs, and view names.
EL is also used to bind form parameters to model objects and, conversely, to render formatted form fields from the properties of a model object. That, however, does not apply when using Web Flow with JSF. In that case, the standard JSF component lifecyle applies.
12.1. Expression Types
An important concept to understand is there are two types of expressions in Web Flow:
12.1.1. Standard Expressions
The first and most common type of expression is the standard expression.
Such expressions are evaluated directly by the EL and need not be enclosed in delimiters, such as \#{}
.
The following example shows such a standard expression:
<evaluate expression="searchCriteria.nextPage()" />
The preceding expression is a standard expression that invokes the nextPage
method on the searchCriteria
variable when evaluated.
If you try to enclose this expression in a special delimiter (such as \#{}
), you get an IllegalArgumentException
.
In this context, the delimiter is seen as redundant.
The only acceptable value for the expression
attribute is a single expression string.
12.1.2. Template Expressions
The second type of expression is a template expression.
A template expression allows mixing of literal text with one or more standard expressions.
Each standard expression block is explicitly surrounded with the \#{}
delimiters.
The following example shows a template expression:
<view-state id="error" view="error-#{externalContext.locale}.xhtml" />
The preceding expression is a template expression.
The result of evaluation is a string that concatenates literal text such as error-
and .xhtml
with the result of evaluating externalContext.locale
.
You need explicit delimiters here to demarcate standard expression blocks within the template.
See the Web Flow XML schema for a complete listing of those XML attributes that accept standard expressions and those that accept template expressions. You can also use F2 in Eclipse (or equivalent shortcuts in other IDEs) to access available documentation when typing out specific flow definition attributes. |
12.2. EL Implementations
Spring Web Flow supports the following EL (Expression Language) implementations:
12.2.1. Spring EL
Web Flow uses the Spring Expression Language (Spring EL). Spring EL was created to provide a single, well-supported expression language for use across all the products in the Spring portfolio.
It is distributed as a separate jar (org.springframework.expression
) in the Spring Framework.
12.2.2. Unified EL
Use of the Unified EL also implies a dependency on el-api
, although that is typically provided by your web container.
Although Spring EL is the default and recommended expression language to use, you can replace it with Unified EL.
To do so, you need the following Spring configuration to plug in the WebFlowELExpressionParser
to the flow-builder-services
:
<webflow:flow-builder-services expression-parser="expressionParser"/>
<bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
<constructor-arg>
<bean class="org.jboss.el.ExpressionFactoryImpl" />
</constructor-arg>
</bean>
Note that, if your application registers custom converters, it is important to ensure WebFlowELExpressionParser
is configured with the conversion service that has those custom converters, as follows:
<webflow:flow-builder-services expression-parser="expressionParser" conversion-service="conversionService"/>
<bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser">
<constructor-arg>
<bean class="org.jboss.el.ExpressionFactoryImpl" />
</constructor-arg>
<property name="conversionService" ref="conversionService"/>
</bean>
<bean id="conversionService" class="somepackage.ApplicationConversionService"/>
12.3. EL Portability
In general, you will find Spring EL and Unified EL to have a very similar syntax.
However, Spring El has some advantages. For example, Spring EL is closely integrated with the type conversion of Spring 3, and that lets you take full advantage of its features. Specifically, the automatic detection of generic types as well as the use of formatting annotations is currently supported only with Spring EL.
Keep in mind the following minor changes when upgrading to Spring EL from Unified EL:
-
Expressions delineated with
${}
in flow definitions must be changed to\#{}
(note the leading backspace character). -
Expressions that test the current event (such as
#{currentEvent == 'submit'}
) must be changed to\#{currentEvent.id == 'submit'}
(note the addition of theid
). -
Resolving properties (such as
#{currentUser.name}
) may cause aNullPointerException
without any checks such as\#{currentUser != null ? currentUser.name : null}
. A much better alternative is the safe navigation operator:\#{currentUser?.name}
.
For more information on Spring EL syntax, see the Language Reference section in the Spring Documentation.
12.4. Special EL Variables
You can reference several implicit variables from within a flow.
Keep in mind this general rule:
You should use variables that refer to data scopes (flowScope
, viewScope
, requestScope
, and so on) only when you assign a new variable to one of the scopes.
For example, when assigning the result of the call to bookingService.findHotels(searchCriteria)
to a new variable called hotels
, you must prefix it with a scope variable to let Web Flow know where you want it stored.
The following example shows how to do so:
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow" ... >
<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />
<view-state id="reviewHotels">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>
</view-state>
</flow>
However, when setting an existing variable (such as searchCriteria
in the following example), you should reference the variable directly without prefixing it with any scope variables, as follows:
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow" ... >
<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />
<view-state id="reviewHotels">
<transition on="sort">
<set name="searchCriteria.sortBy" value="requestParameters.sortBy" />
</transition>
</view-state>
</flow>
The following is the list of implicit variables you can reference within a flow definition:
12.4.1. The flowScope
Variable
You can use the flowScope
to assign a flow variable.
Flow scope gets allocated when a flow starts and destroyed when the flow ends.
With the default implementation, any objects stored in flow scope need to be serializable.
The following listing defines a flowScope
variable:
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />
12.4.2. The viewScope
Variable
You can use the viewScope
to assign a view variable.
View scope gets allocated when a view-state
is entered and destroyed when the state exits.
View scope is referenceable only from within a view-state
.
With the default implementation, any objects stored in view scope need to be serializable.
The following listing defines a viewScope
variable:
<on-render>
<evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
result-type="dataModel" />
</on-render>
12.4.3. The requestScope
Variable
You can use requestScope
to assign a request variable.
Request scope gets allocated when a flow is called and destroyed when the flow returns.
The following listing defines a requestScope
variable:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
12.4.4. The flashScope
Variable
You can use flashScope
to assign a flash variable.
Flash scope gets allocated when a flow starts, cleared after every view render, and destroyed when the flow ends.
With the default implementation, any objects stored in flash scope need to be serializable.
The following listing defines a flashScope
variable:
<set name="flashScope.statusMessage" value="'Booking confirmed'" />
12.4.5. The conversationScope
Variable
You can use conversationScope
to assign a conversation variable.
Conversation scope gets allocated when a top-level flow starts and destroyed when the top-level flow ends.
Conversation scope is shared by a top-level flow and all of its sub-flows.
With the default implementation, conversation-scoped objects are stored in the HTTP session and should generally be serializable to account for typical session replication.
The following listing defines a conversationScope
variable:
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />
12.4.6. The requestParameters
Variable
The requestParameters
variable accesses a client request parameter, as follows:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
12.4.7. The currentEvent
Variable
The currentEvent
variable accesses attributes of the current Event
, as follows:
<evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />
12.4.8. The currentUser
Variable
The currentUser
variable accesses the authenticated Principal
, as follows:
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
12.4.9. The messageContext
Variable
The messageContext
variable accesses a context to retrieve and create flow execution messages, including error and success messages.
See the MessageContext
Javadocs for more information.
The following example uses the messageContext
variable:
<evaluate expression="bookingValidator.validate(booking, messageContext)" />
12.4.10. The resourceBundle
Variable
The resourceBundle
variable accesses a message resource, as follows:
<set name="flashScope.successMessage" value="resourceBundle.successMessage" />
12.4.11. The flowRequestContext
Variable
The flowRequestContext
variable accesses the RequestContext
API, which is a representation of the current flow request.
See the API Javadocs for more information.
12.4.12. The flowExecutionContext
Variable
The flowExecutionContext
variable accesses the FlowExecutionContext
API, which is a representation of the current flow state.
See the API Javadocs for more information.
12.4.13. The flowExecutionUrl
Variable
The flowExecutionUrl
variable accesses the context-relative URI for the current flow execution view-state.
12.4.14. The externalContext
Variable
The externalContext
variable accesses the client environment, including user session attributes.
See the ExternalContext
API JavaDocs for more information.
The following example uses the externalContext
variable:
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
result="viewScope.hotels" />
12.5. Scope Searching Algorithm
As mentioned earlier in this section, when assigning a variable in one of the flow scopes, referencing that scope is required. The following example shows how to do so:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
When you are merely accessing a variable in one of the scopes, referencing the scope is optional, as follows:
<evaluate expression="entityManager.persist(booking)" />
When no scope is specified, as in the use of booking
shown earlier, a scope searching algorithm is used.
The algorithm looks in the request, flash, view, flow, and conversation scopes for the variable.
If no such variable is found, an EvaluationException
is thrown.