373 lines
18 KiB
XML
373 lines
18 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
|
<chapter id="el">
|
|
<title>Expression Language (EL)</title>
|
|
<sect1 id="el-introduction">
|
|
<title>Introduction</title>
|
|
<para>
|
|
Web Flow uses EL to access its data model and to invoke actions.
|
|
This chapter will familiarize you with EL syntax, configuration, and special EL variables you can reference from your flow definition.
|
|
</para>
|
|
<para>
|
|
EL is used for many things within a flow including:
|
|
</para>
|
|
<orderedlist>
|
|
<listitem><para>Access client data such as declaring flow inputs or referencing request parameters.</para></listitem>
|
|
<listitem><para>Access data in Web Flow's <code>RequestContext</code> such as <code>flowScope</code> or <code>currentEvent</code>.</para></listitem>
|
|
<listitem><para>Invoke methods on Spring-managed objects through actions.</para></listitem>
|
|
<listitem><para>Resolve expressions such as state transition criteria, subflow ids, and view names.</para></listitem>
|
|
</orderedlist>
|
|
<para>
|
|
EL is also used to bind form parameters to model objects and reversely to render formatted form fields from the properties of a model object.
|
|
That however does not apply when using Web Flow with JSF in which case the standard JSF component lifecyle applies.
|
|
</para>
|
|
<sect2 id="el-types">
|
|
<title>Expression types</title>
|
|
<para>
|
|
An important concept to understand is there are two types of expressions in Web Flow: standard expressions and template expressions.
|
|
</para>
|
|
<sect3 id="el-types-eval">
|
|
<title>Standard Expressions</title>
|
|
<para>
|
|
The first and most common type of expression is the <emphasis>standard expression</emphasis>.
|
|
Such expressions are evaluated directly by the EL and need not be enclosed in delimiters like <code>#{}</code>.
|
|
For example:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="searchCriteria.nextPage()" />]]>
|
|
</programlisting>
|
|
<para>
|
|
The expression above is a standard expression that invokes the <code>nextPage</code> method on the <code>searchCriteria</code> variable when evaluated.
|
|
If you attempt to enclose this expression in a special delimiter like <code>#{}</code> you will get an <code>IllegalArgumentException</code>.
|
|
In this context the delimiter is seen as redundant.
|
|
The only acceptable value for the <code>expression</code> attribute is an single expression string.
|
|
</para>
|
|
</sect3>
|
|
<sect3 id="el-types-template">
|
|
<title>Template expressions</title>
|
|
<para>
|
|
The second type of expression is a <emphasis>template expression</emphasis>.
|
|
A template expression allows mixing of literal text with one or more standard expressions.
|
|
Each standard expression block is explicitly surrounded with the <code>#{}</code> delimiters.
|
|
For example:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="error" view="error-#{externalContext.locale}.xhtml" />]]>
|
|
</programlisting>
|
|
<para>
|
|
The expression above is a template expression.
|
|
The result of evaluation will be a string that concatenates literal text such as <code>error-</code> and <code>.xhtml</code> with the result of evaluating <code>externalContext.locale</code>.
|
|
As you can see, explicit delimiters are necessary here to demarcate standard expression blocks within the template.
|
|
</para>
|
|
<note>
|
|
<para>
|
|
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 shortcut in other IDEs) to access available documentation when typing out specific flow definition attributes.
|
|
</para>
|
|
</note>
|
|
</sect3>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="el-language-choices">
|
|
<title>EL Implementations</title>
|
|
<sect2 id="el-spring-el">
|
|
<title>Spring EL</title>
|
|
<para>
|
|
Starting with version 2.1 Web Flow uses the <ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html">Spring Expression Language</ulink> (Spring EL).
|
|
Spring EL was created to provide is a single, well-supported expression language for use across all the products in the Spring portfolio.
|
|
It is distributed as a separate jar <code>org.springframework.expression</code> in the Spring Framework.
|
|
Existing applications will need to remove dependencies on <code>org.jboss.el</code> or <code>org.ognl</code> and use <code>org.springframework.expression</code> instead.
|
|
See the section below on EL Portability for other notes on upgrading.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="el-unified-el">
|
|
<title>Unified EL</title>
|
|
<para>
|
|
In Web Flow 2.0 <ulink url="http://en.wikipedia.org/wiki/Unified_Expression_Language">Unified EL</ulink> was the default expression language with <code>jboss-el</code> as the implementation.
|
|
Use of Unified EL also implies a dependency on <code>el-api</code> although that is typically <emphasis>provided</emphasis> by your web container.
|
|
Tomcat 6 includes it, for example.
|
|
Spring EL is the default and recommended expression language to use.
|
|
However it is possible to replace it with Unified EL if you wish to do so.
|
|
You need the following Spring configuration to plug in the <code>WebFlowELExpressionParser</code> to the <code>flow-builder-services</code>:
|
|
<programlisting language="xml"><![CDATA[
|
|
<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>]]>
|
|
</programlisting>
|
|
Note that if your application is registering custom converters it's important to ensure the WebFlowELExpressionParser is configured with the conversion service that has those custom converters.
|
|
<programlisting language="xml"><![CDATA[
|
|
<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"/>]]>
|
|
</programlisting>
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="el-ognl">
|
|
<title>OGNL</title>
|
|
<para>
|
|
<ulink url="http://www.ognl.org">OGNL</ulink> is the third supported expression language.
|
|
OGNL is the EL most familiar to Web Flow version 1.0 users.
|
|
Please refer to the <ulink url="http://www.ognl.org/2.6.9/Documentation/html/LanguageGuide/index.html">OGNL language guide</ulink> for specifics on its EL syntax.
|
|
If you wish to use OGNL this is the Spring configuration necessary to plug it in:
|
|
<programlisting language="xml"><![CDATA[
|
|
<webflow:flow-builder-services expression-parser="expressionParser"/>
|
|
|
|
<bean id="expressionParser" class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser"/>]]>
|
|
</programlisting>
|
|
Note that if your application is registering custom converters it's important to ensure the WebFlowOgnlExpressionParser is configured with the conversion service that has those custom converters.
|
|
<programlisting language="xml"><![CDATA[
|
|
<webflow:flow-builder-services expression-parser="expressionParser" conversion-service="conversionService"/>
|
|
|
|
<bean id="expressionParser" class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser">
|
|
<property name="conversionService" ref="conversionService"/>
|
|
</bean>
|
|
|
|
<bean id="conversionService" class="somepackage.ApplicationConversionService"/>]]>
|
|
</programlisting>
|
|
</para>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="el-portability">
|
|
<title>EL portability</title>
|
|
<para>
|
|
In general, you will find Spring EL, Unified EL and OGNL to have a very similar syntax.
|
|
</para>
|
|
<para>
|
|
Note however there are some advantages to Spring EL.
|
|
For example Spring EL is closely integrated with the type conversion of Spring 3 and that allows you to take full advantage of its features.
|
|
Specifically the automatic detection of generic types as well as the use of formatting annotations is currently supported with Spring EL only.
|
|
</para>
|
|
<para>
|
|
There are some minor changes to keep in mind when upgrading to Spring EL from Unified EL or OGNL as follows:
|
|
<orderedlist>
|
|
<listitem><para>Expressions deliniated with <code>${}</code> in flow definitions must be changed to <code>#{}</code>.</para></listitem>
|
|
<listitem><para>Expressions testing the current event <code>#{currentEvent == 'submit'}</code> must be changed to <code>#{currentEvent.id == 'submit'}</code>.</para></listitem>
|
|
<listitem>
|
|
<para>
|
|
Resolving properties such as <code>#{currentUser.name}</code> may cause NullPointerException without any checks such as <code>#{currentUser != null ? currentUser.name : null}</code>.
|
|
A much better alternative though is the safe navigation operator <code>#{currentUser?.name}</code>.
|
|
</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
For more information on Spring EL syntax please refer to the <ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html#expressions-language-ref">Language Reference</ulink> section in the Spring Documentation.
|
|
</para>
|
|
</sect1>
|
|
<sect1 id="el-variables">
|
|
<title>Special EL variables</title>
|
|
<para>
|
|
There are several implicit variables you may reference from within a flow.
|
|
These variables are discussed in this section.
|
|
</para>
|
|
<para>
|
|
Keep in mind this general rule.
|
|
Variables referring to data scopes (flowScope, viewScope, requestScope, etc.) should only be used when assigning a new variable to one of the scopes.
|
|
</para>
|
|
<para>
|
|
For example when assigning the result of the call to <code>bookingService.findHotels(searchCriteria)</code> to a new variable called "hotels" you must prefix it with a scope variable in order to let Web Flow know where you want it stored:
|
|
<programlisting language="xml"><![CDATA[
|
|
<?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>]]>
|
|
</programlisting>
|
|
However when setting an existing variable such as "searchCriteria" in the example below, you reference the variable directly without prefixing it with any scope variables:
|
|
<programlisting language="xml"><![CDATA[
|
|
<?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>]]>
|
|
</programlisting>
|
|
</para>
|
|
<para>
|
|
The following is the list of implicit variables you can reference within a flow definition:
|
|
</para>
|
|
<sect2 id="el-variable-flowScope">
|
|
<title>flowScope</title>
|
|
<para>
|
|
Use <code>flowScope</code> 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.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-viewScope">
|
|
<title>viewScope</title>
|
|
<para>
|
|
Use <code>viewScope</code> to assign a view variable.
|
|
View scope gets allocated when a <code>view-state</code> enters and destroyed when the state exits.
|
|
View scope is <emphasis>only</emphasis> referenceable from within a <code>view-state</code>. With the
|
|
default implementation, any objects stored in view scope need to be Serializable.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<on-render>
|
|
<evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
|
|
result-type="dataModel" />
|
|
</on-render>]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-requestScope">
|
|
<title>requestScope</title>
|
|
<para>
|
|
Use <code>requestScope</code> to assign a request variable.
|
|
Request scope gets allocated when a flow is called and destroyed when the flow returns.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-flashScope">
|
|
<title>flashScope</title>
|
|
<para>
|
|
Use <code>flashScope</code> 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.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<set name="flashScope.statusMessage" value="'Booking confirmed'" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-conversationScope">
|
|
<title>conversationScope</title>
|
|
<para>
|
|
Use <code>conversationScope</code> 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 subflows. With the default
|
|
implementation, conversation scoped objects are stored in the HTTP session and should generally be
|
|
Serializable to account for typical session replication.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-requestParameters">
|
|
<title>requestParameters</title>
|
|
<para>
|
|
Use <code>requestParameters</code> to access a client request parameter:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-currentEvent">
|
|
<title>currentEvent</title>
|
|
<para>
|
|
Use <code>currentEvent</code> to access attributes of the current <code>Event</code>:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-currentUser">
|
|
<title>currentUser</title>
|
|
<para>
|
|
Use <code>currentUser</code> to access the authenticated <code>Principal</code>:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
|
|
result="flowScope.booking" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-messageContext">
|
|
<title>messageContext</title>
|
|
<para>
|
|
Use <code>messageContext</code> to access a context for retrieving and creating flow execution messages, including error and success messages.
|
|
See the <code>MessageContext</code> Javadocs for more information.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="bookingValidator.validate(booking, messageContext)" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-resourceBundle">
|
|
<title>resourceBundle</title>
|
|
<para>
|
|
Use <code>resourceBundle</code> to access a message resource.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<set name="flashScope.successMessage" value="resourceBundle.successMessage" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="el-variable-requestContext">
|
|
<title>flowRequestContext</title>
|
|
<para>
|
|
Use <code>flowRequestContext</code> to access the <code>RequestContext</code> API, which is a representation of the current flow request.
|
|
See the API Javadocs for more information.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="el-variable-flowExecutionContext">
|
|
<title>flowExecutionContext</title>
|
|
<para>
|
|
Use <code>flowExecutionContext</code> to access the <code>FlowExecutionContext</code> API, which is a representation of the current flow state.
|
|
See the API Javadocs for more information.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="el-variable-flowExecutionUrl">
|
|
<title>flowExecutionUrl</title>
|
|
<para>
|
|
Use <code>flowExecutionUrl</code> to access the context-relative URI for the current flow execution view-state.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="el-variable-externalContext">
|
|
<title>externalContext</title>
|
|
<para>
|
|
Use <code>externalContext</code> to access the client environment, including user session attributes.
|
|
See the <code>ExternalContext</code> API JavaDocs for more information.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
|
|
result="viewScope.hotels" />]]>
|
|
</programlisting>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="el-scope-searching">
|
|
<title>Scope searching algorithm</title>
|
|
<para>
|
|
As mentioned earlier in this section when assigning a variable in one of the flow scopes, referencing that scope is required.
|
|
For example:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />]]>
|
|
</programlisting>
|
|
<para>
|
|
When simply accessing a variable in one of the scopes, referencing the scope is optional.
|
|
For example:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<evaluate expression="entityManager.persist(booking)" />]]>
|
|
</programlisting>
|
|
<para>
|
|
When no scope is specified, like in the use of <code>booking</code> above, a scope searching algorithm is used.
|
|
The algorithm will look in request, flash, view, flow, and conversation scope for the variable.
|
|
If no such variable is found, an <code>EvaluationException</code> will be thrown.
|
|
</para>
|
|
</sect1>
|
|
</chapter> |