Add reference docs tasks to Gradle build
- Move spring-webflow-reference to src/reference - Upgrade to Docbook 5 - Apply docbook plugin - Add tasks for generating distribution zip files
This commit is contained in:
467
src/reference/actions.xml
Normal file
467
src/reference/actions.xml
Normal file
@@ -0,0 +1,467 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="actions">
|
||||
<title>Executing actions</title>
|
||||
<sect1 id="actions-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
This chapter shows you how to use the <code>action-state</code> element to control the execution of an action at a point within a flow.
|
||||
It will also show how to use the <code>decision-state</code> element to make a flow routing decision.
|
||||
Finally, several examples of invoking actions from the various points possible within a flow will be discussed.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="action-state">
|
||||
<title>Defining action states</title>
|
||||
<para>
|
||||
Use the <code>action-state</code> element when you wish to invoke an action, then transition to another state based on the action's outcome:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<action-state id="moreAnswersNeeded">
|
||||
<evaluate expression="interview.moreAnswersNeeded()" />
|
||||
<transition on="yes" to="answerQuestions" />
|
||||
<transition on="no" to="finish" />
|
||||
</action-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
The full example below illustrates a interview flow that uses the action-state above to determine if more answers are needed to complete the interview:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<flow xmlns="http://www.springframework.org/schema/webflow"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow
|
||||
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
|
||||
|
||||
<on-start>
|
||||
<evaluate expression="interviewFactory.createInterview()" result="flowScope.interview" />
|
||||
</on-start>
|
||||
|
||||
<view-state id="answerQuestions" model="questionSet">
|
||||
<on-entry>
|
||||
<evaluate expression="interview.getNextQuestionSet()" result="viewScope.questionSet" />
|
||||
</on-entry>
|
||||
<transition on="submitAnswers" to="moreAnswersNeeded">
|
||||
<evaluate expression="interview.recordAnswers(questionSet)" />
|
||||
</transition>
|
||||
</view-state>
|
||||
|
||||
<action-state id="moreAnswersNeeded">
|
||||
<evaluate expression="interview.moreAnswersNeeded()" />
|
||||
<transition on="yes" to="answerQuestions" />
|
||||
<transition on="no" to="finish" />
|
||||
</action-state>
|
||||
|
||||
<end-state id="finish" />
|
||||
|
||||
</flow>]]>
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="decision-state">
|
||||
<title>Defining decision states</title>
|
||||
<para>
|
||||
Use the <code>decision-state</code> element as an alternative to the action-state to make a routing decision using a convenient if/else syntax.
|
||||
The example below shows the <code>moreAnswersNeeded</code> state above now implemented as a decision state instead of an action-state:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<decision-state id="moreAnswersNeeded">
|
||||
<if test="interview.moreAnswersNeeded()" then="answerQuestions" else="finish" />
|
||||
</decision-state>]]>
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="action-outcome-events">
|
||||
<title>Action outcome event mappings</title>
|
||||
<para>
|
||||
Actions often invoke methods on plain Java objects.
|
||||
When called from action-states and decision-states, these method return values can be used to drive state transitions.
|
||||
Since transitions are triggered by events, a method return value must first be mapped to an Event object.
|
||||
The following table describes how common return value types are mapped to Event objects:
|
||||
</para>
|
||||
<table id="event-mapping-table">
|
||||
<title>Action method return value to event id mappings</title>
|
||||
<tgroup cols="2">
|
||||
<colspec colname="Method return type" colwidth="*"/>
|
||||
<colspec colname="Event identifier" colwidth="3*"/>
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Method return type</entry>
|
||||
<entry>Mapped Event identifier expression</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>java.lang.String</entry>
|
||||
<entry>the String value</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>java.lang.Boolean</entry>
|
||||
<entry>yes (for true), no (for false)</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>java.lang.Enum</entry>
|
||||
<entry>the Enum name</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>any other type</entry>
|
||||
<entry>success</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
<para>
|
||||
This is illustrated in the example action state below, which invokes a method that returns a boolean value:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<action-state id="moreAnswersNeeded">
|
||||
<evaluate expression="interview.moreAnswersNeeded()" />
|
||||
<transition on="yes" to="answerQuestions" />
|
||||
<transition on="no" to="finish" />
|
||||
</action-state>]]>
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="action-implementations">
|
||||
<title>Action implementations</title>
|
||||
<para>
|
||||
While writing action code as POJO logic is the most common, there are several other action implementation options.
|
||||
Sometimes you need to write action code that needs access to the flow context.
|
||||
You can always invoke a POJO and pass it the flowRequestContext as an EL variable.
|
||||
Alternatively, you may implement the <code>Action</code> interface or extend from the <code>MultiAction</code> base class.
|
||||
These options provide stronger type safety when you have a natural coupling between your action code and Spring Web Flow APIs.
|
||||
Examples of each of these approaches are shown below.
|
||||
</para>
|
||||
<sect2>
|
||||
<title>Invoking a POJO action</title>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<evaluate expression="pojoAction.method(flowRequestContext)" />]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class PojoAction {
|
||||
public String method(RequestContext context) {
|
||||
...
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Invoking a custom Action implementation</title>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<evaluate expression="customAction" />]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class CustomAction implements Action {
|
||||
public Event execute(RequestContext context) {
|
||||
...
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Invoking a MultiAction implementation</title>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<evaluate expression="multiAction.actionMethod1" />
|
||||
]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class CustomMultiAction extends MultiAction {
|
||||
public Event actionMethod1(RequestContext context) {
|
||||
...
|
||||
}
|
||||
|
||||
public Event actionMethod2(RequestContext context) {
|
||||
...
|
||||
}
|
||||
|
||||
...
|
||||
}]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="action-exceptions">
|
||||
<title>Action exceptions</title>
|
||||
<para>
|
||||
Actions often invoke services that encapsulate complex business logic.
|
||||
These services may throw business exceptions that the action code should handle.
|
||||
</para>
|
||||
<sect2>
|
||||
<title>Handling a business exception with a POJO action</title>
|
||||
<para>
|
||||
The following example invokes an action that catches a business exception, adds a error message to the context, and returns a result event identifier.
|
||||
The result is treated as a flow event which the calling flow can then respond to.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<evaluate expression="bookingAction.makeBooking(booking, flowRequestContext)" />]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class BookingAction {
|
||||
public String makeBooking(Booking booking, RequestContext context) {
|
||||
try {
|
||||
BookingConfirmation confirmation = bookingService.make(booking);
|
||||
context.getFlowScope().put("confirmation", confirmation);
|
||||
return "success";
|
||||
} catch (RoomNotAvailableException e) {
|
||||
context.addMessage(new MessageBuilder().error().
|
||||
.defaultText("No room is available at this hotel").build());
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Handling a business exception with a MultiAction</title>
|
||||
<para>
|
||||
The following example is functionally equivlant to the last, but implemented as a MultiAction instead of a POJO action.
|
||||
The MultiAction requires its action methods to be of the signature <code>Event ${methodName}(RequestContext)</code>, providing stronger type safety, while a POJO action allows for more freedom.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<evaluate expression="bookingAction.makeBooking" />]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class BookingAction extends MultiAction {
|
||||
public Event makeBooking(RequestContext context) {
|
||||
try {
|
||||
Booking booking = (Booking) context.getFlowScope().get("booking");
|
||||
BookingConfirmation confirmation = bookingService.make(booking);
|
||||
context.getFlowScope().put("confirmation", confirmation);
|
||||
return success();
|
||||
} catch (RoomNotAvailableException e) {
|
||||
context.getMessageContext().addMessage(new MessageBuilder().error().
|
||||
.defaultText("No room is available at this hotel").build());
|
||||
return error();
|
||||
}
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="action-examples">
|
||||
<title>Other Action execution examples</title>
|
||||
<sect2 id="action-on-start">
|
||||
<title>on-start</title>
|
||||
<para>
|
||||
The following example shows an action that creates a new Booking object by invoking a method on a service:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<flow xmlns="http://www.springframework.org/schema/webflow"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow
|
||||
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
|
||||
|
||||
<input name="hotelId" />
|
||||
|
||||
<on-start>
|
||||
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
|
||||
result="flowScope.booking" />
|
||||
</on-start>
|
||||
|
||||
</flow>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="action-on-state-entry">
|
||||
<title>on-entry</title>
|
||||
<para>
|
||||
The following example shows a state entry action that sets the special <code>fragments</code> variable that causes the view-state to render a partial fragment of its view:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
|
||||
<on-entry>
|
||||
<render fragments="hotelSearchForm" />
|
||||
</on-entry>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="action-on-state-exit">
|
||||
<title>on-exit</title>
|
||||
<para>
|
||||
The following example shows a state exit action that releases a lock on a record being edited:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="editOrder">
|
||||
<on-entry>
|
||||
<evaluate expression="orderService.selectForUpdate(orderId, currentUser)"
|
||||
result="viewScope.order" />
|
||||
</on-entry>
|
||||
<transition on="save" to="finish">
|
||||
<evaluate expression="orderService.update(order, currentUser)" />
|
||||
</transition>
|
||||
<on-exit>
|
||||
<evaluate expression="orderService.releaseLock(order, currentUser)" />
|
||||
</on-exit>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="on-end">
|
||||
<title>on-end</title>
|
||||
<para>
|
||||
The following example shows the equivalent object locking behavior using flow start and end actions:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<flow xmlns="http://www.springframework.org/schema/webflow"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow
|
||||
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
|
||||
|
||||
<input name="orderId" />
|
||||
|
||||
<on-start>
|
||||
<evaluate expression="orderService.selectForUpdate(orderId, currentUser)"
|
||||
result="flowScope.order" />
|
||||
</on-start>
|
||||
|
||||
<view-state id="editOrder">
|
||||
<transition on="save" to="finish">
|
||||
<evaluate expression="orderService.update(order, currentUser)" />
|
||||
</transition>
|
||||
</view-state>
|
||||
|
||||
<on-end>
|
||||
<evaluate expression="orderService.releaseLock(order, currentUser)" />
|
||||
</on-end>
|
||||
|
||||
</flow>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="action-on-render">
|
||||
<title>on-render</title>
|
||||
<para>
|
||||
The following example shows a render action that loads a list of hotels to display before the view is rendered:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="reviewHotels">
|
||||
<on-render>
|
||||
<evaluate expression="bookingService.findHotels(searchCriteria)"
|
||||
result="viewScope.hotels" result-type="dataModel" />
|
||||
</on-render>
|
||||
<transition on="select" to="reviewHotel">
|
||||
<set name="flowScope.hotel" value="hotels.selectedRow" />
|
||||
</transition>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="action-on-transition">
|
||||
<title>on-transition</title>
|
||||
<para>
|
||||
The following example shows a transition action adds a subflow outcome event attribute to a collection:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<subflow-state id="addGuest" subflow="createGuest">
|
||||
<transition on="guestCreated" to="reviewBooking">
|
||||
<evaluate expression="booking.guestList.add(currentEvent.attributes.newGuest)" />
|
||||
</transition>
|
||||
</subfow-state>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="named-actions">
|
||||
<title>Named actions</title>
|
||||
<para>
|
||||
The following example shows how to execute a chain of actions in an action-state.
|
||||
The name of each action becomes a qualifier for the action's result event.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<action-state id="doTwoThings">
|
||||
<evaluate expression="service.thingOne()">
|
||||
<attribute name="name" value="thingOne" />
|
||||
</evaluate>
|
||||
<evaluate expression="service.thingTwo()">
|
||||
<attribute name="name" value="thingTwo" />
|
||||
</evaluate>
|
||||
<transition on="thingTwo.success" to="showResults" />
|
||||
</action-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In this example, the flow will transition to <code>showResults</code> when <code>thingTwo</code>
|
||||
completes successfully.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="streaming-actions">
|
||||
<title>Streaming actions</title>
|
||||
<para>
|
||||
Sometimes an Action needs to stream a custom response back to the client.
|
||||
An example might be a flow that renders a PDF document when handling a print event.
|
||||
This can be achieved by having the action stream the content then record "Response Complete" status on the ExternalContext.
|
||||
The responseComplete flag tells the pausing view-state not to render the response because another object has taken care of it.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="reviewItinerary">
|
||||
<transition on="print">
|
||||
<evaluate expression="printBoardingPassAction" />
|
||||
</transition>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class PrintBoardingPassAction extends AbstractAction {
|
||||
public Event doExecute(RequestContext context) {
|
||||
// stream PDF content here...
|
||||
// - Access HttpServletResponse by calling context.getExternalContext().getNativeResponse();
|
||||
// - Mark response complete by calling context.getExternalContext().recordResponseComplete();
|
||||
return success();
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In this example, when the print event is raised the flow will call the printBoardingPassAction.
|
||||
The action will render the PDF then mark the response as complete.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="file-upload">
|
||||
<title>Handling File Uploads</title>
|
||||
<para>
|
||||
Another common task is to use Web Flow to handle multipart file uploads in combination with Spring MVC's
|
||||
<code>MultipartResolver</code>. Once the resolver is set up correctly <ulink url="http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html#mvc-multipart">as described here</ulink> and the submitting
|
||||
HTML form is configured with <code>enctype="multipart/form-data"</code>, you can easily handle the file upload in a
|
||||
transition action.
|
||||
</para>
|
||||
<para>
|
||||
Note that the File Upload example below below is not relevant when using Web Flow with JSF.
|
||||
Check the documentation of your JSF component library to see what file upload components it provides.
|
||||
</para>
|
||||
<para>
|
||||
Given a form such as:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<form:form modelAttribute="fileUploadHandler" enctype="multipart/form-data">
|
||||
Select file: <input type="file" name="file"/>
|
||||
<input type="submit" name="_eventId_upload" value="Upload" />
|
||||
</form:form>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
and a backing object for handling the upload such as:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
package org.springframework.webflow.samples.booking;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
public class FileUploadHandler {
|
||||
|
||||
private transient MultipartFile file;
|
||||
|
||||
public void processFile() {
|
||||
//Do something with the MultipartFile here
|
||||
}
|
||||
|
||||
public void setFile(MultipartFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
you can process the upload using a transition action as in the following example:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="uploadFile" model="uploadFileHandler">
|
||||
<var name="fileUploadHandler" class="org.springframework.webflow.samples.booking.FileUploadHandler" />
|
||||
<transition on="upload" to="finish" >
|
||||
<evaluate expression="fileUploadHandler.processFile()"/>
|
||||
</transition>
|
||||
<transition on="cancel" to="finish" bind="false"/>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
The <code>MultipartFile</code> will be bound to the <code>FileUploadHandler</code> bean as
|
||||
part of the normal form binding process so that it will be available to process during the
|
||||
execution of the transition action.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user