KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > webflow > engine > ActionState


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.webflow.engine;
17
18 import java.util.Iterator JavaDoc;
19
20 import org.springframework.core.style.StylerUtils;
21 import org.springframework.core.style.ToStringCreator;
22 import org.springframework.webflow.execution.Action;
23 import org.springframework.webflow.execution.Event;
24 import org.springframework.webflow.execution.FlowExecutionException;
25 import org.springframework.webflow.execution.RequestContext;
26 import org.springframework.webflow.execution.ViewSelection;
27
28 /**
29  * A transitionable state that executes one or more actions when entered. When
30  * the action(s) are executed this state responds to their result(s) to decide
31  * what state to transition to next.
32  * <p>
33  * If more than one action is configured they are executed in an ordered chain
34  * until one returns a result event that matches a state transition out of
35  * this state. This is a form of the Chain of Responsibility (CoR) pattern.
36  * <p>
37  * The result of an action's execution is typically the criteria for a
38  * transition out of this state. Additional information in the current
39  * {@link RequestContext} may also be tested as part of custom transitional
40  * criteria, allowing for sophisticated transition expressions that reason on
41  * contextual state.
42  * <p>
43  * Each action executed by this action state may be provisioned with a set of
44  * arbitrary execution properties. These properties are made available to the
45  * action at execution time and may be used to influence action execution
46  * behavior.
47  * <p>
48  * Common action execution properties include:
49  * <p>
50  * <table border="1">
51  * <th>Property</th>
52  * <th>Description</th>
53  * <tr>
54  * <td valign="top">name</td>
55  * <td>The 'name' property is used as a qualifier for an action's result event,
56  * and is typically used to allow the flow to respond to a specific action's
57  * outcome within a larger action chain. For example, if an action named
58  * <code>myAction</code> returns a <code>success</code> result, a transition
59  * that matches on event <code>myAction.success</code> will be searched, and
60  * if found, executed. If this action is not assigned a name a transition for
61  * the base <code>success</code> event will be searched and if found,
62  * executed.<br>
63  * This is useful in situations where you want to execute actions in an ordered
64  * chain as part of one action state, and wish to transition on the result of
65  * the last one in the chain. For example:
66  *
67  * <pre>
68  * &lt;action-state id=&quot;setupForm&quot;&gt;
69  * &lt;action name=&quot;setup&quot; bean=&quot;myAction&quot; method=&quot;setupForm&quot;/&gt;
70  * &lt;action name=&quot;referenceData&quot; bean=&quot;myAction&quot; method=&quot;setupReferenceData&quot;/&gt;
71  * &lt;transition on=&quot;referenceData.success&quot; to=&quot;displayForm&quot;/&gt;
72  * &lt;/action-state&gt;
73  * </pre>
74  *
75  * When the 'setupForm' state above is entered, the 'setup' action will execute,
76  * followed by the 'referenceData' action. After 'referenceData' execution, the
77  * flow will then respond to the 'referenceData.success' event by transitioning
78  * to the 'displayForm' state. The 'setup.success' event that was signaled by
79  * the 'setup' action will effectively be ignored.</td>
80  * <tr>
81  * <td valign="top">method</td>
82  * <td>The 'method' property is the name of a target method on a
83  * <code>{@link org.springframework.webflow.action.MultiAction}</code> to
84  * execute. In the MultiAction scenario the named method must have the signature
85  * <code>public Event ${method}(RequestContext) throws Exception</code>.
86  * As an example of this scenario, a method property with value <code>setupForm</code>
87  * would bind to a method on a MultiAction instance with the signature:
88  * <code>public Event setupForm(RequestContext context)</code>. <br>
89  * As an alternative to a MultiAction method binding, this action state may
90  * excute a
91  * {@link org.springframework.webflow.action.AbstractBeanInvokingAction bean invoking action}
92  * that invokes a method on a POJO (Plain Old Java Object). If the method
93  * signature accepts arguments those arguments may be specified by using the
94  * format:
95  *
96  * <pre>
97  * methodName(${arg1}, ${arg2}, ...)
98  * </pre>
99  *
100  * Argument ${expressions} are evaluated against the current
101  * <code>RequestContext</code>, allowing for data stored in flow scope or
102  * request scope to be passed as arguments to the POJO. In addition, POJO return
103  * values may be exposed to the flow automatically. See the bean invoking action
104  * type hierarchy for more information. </td>
105  * </tr>
106  * </table>
107  *
108  * @see org.springframework.webflow.execution.Action
109  * @see org.springframework.webflow.action.MultiAction
110  * @see org.springframework.webflow.action.AbstractBeanInvokingAction
111  *
112  * @author Keith Donald
113  * @author Erwin Vervaet
114  */

115 public class ActionState extends TransitionableState {
116
117     /**
118      * The list of actions to be executed when this state is entered.
119      */

120     private ActionList actionList = new ActionList();
121
122     /**
123      * Creates a new action state.
124      * @param flow the owning flow
125      * @param id the state identifier (must be unique to the flow)
126      * @throws IllegalArgumentException when this state cannot be added to given flow,
127      * e.g. beasue the id is not unique
128      * @see #getActionList()
129      */

130     public ActionState(Flow flow, String JavaDoc id) throws IllegalArgumentException JavaDoc {
131         super(flow, id);
132     }
133
134     /**
135      * Returns the list of actions executable by this action state. The
136      * returned list is mutable.
137      * @return the state action list
138      */

139     public ActionList getActionList() {
140         return actionList;
141     }
142
143     /*
144      * Overrides getRequiredTransition(RequestContext) to throw a local
145      * NoMatchingActionResultTransitionException if a transition on the
146      * occurence of an action result event cannot be matched. Used to facilitate
147      * an action invocation chain.
148      * <p>Note that we cannot catch NoMatchingTransitionException since that could lead to unwanted
149      * situations where we're catching an exception that's generated by another
150      * state, e.g. because of a configuration error!
151      */

152     public Transition getRequiredTransition(RequestContext context) throws NoMatchingTransitionException {
153         Transition transition = getTransitionSet().getTransition(context);
154         if (transition == null) {
155             throw new NoMatchingActionResultTransitionException(this, context.getLastEvent());
156         }
157         return transition;
158     }
159
160     /**
161      * Specialization of State's <code>doEnter</code> template method that
162      * executes behaviour specific to this state type in polymorphic fashion.
163      * <p>
164      * This implementation iterates over each configured <code>Action</code>
165      * instance and executes it. Execution continues until an
166      * <code>Action</code> returns a result event that matches a transition in
167      * this request context, or the set of all actions is exhausted.
168      * @param context the control context for the currently executing flow, used
169      * by this state to manipulate the flow execution
170      * @return a view selection signaling that control should be returned to the
171      * client and a view rendered
172      * @throws FlowExecutionException if an exception occurs in this state
173      */

174     protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
175         int executionCount = 0;
176         String JavaDoc[] eventIds = new String JavaDoc[actionList.size()];
177         Iterator JavaDoc it = actionList.iterator();
178         while (it.hasNext()) {
179             Action action = (Action)it.next();
180             Event event = ActionExecutor.execute(action, context);
181             if (event != null) {
182                 eventIds[executionCount] = event.getId();
183                 try {
184                     // will check both local state transitions and global transitions
185
return context.signalEvent(event);
186                 }
187                 catch (NoMatchingActionResultTransitionException e) {
188                     if (logger.isDebugEnabled()) {
189                         logger.debug("Action execution ["
190                                 + (executionCount + 1)
191                                 + "] resulted in no matching transition on event '"
192                                 + event.getId()
193                                 + "'"
194                                 + (it.hasNext() ? ": proceeding to the next action in the list"
195                                         : ": action list exhausted"));
196                     }
197                 }
198             }
199             else {
200                 if (logger.isDebugEnabled()) {
201                     logger.debug("Action execution ["
202                             + (executionCount + 1)
203                             + "] returned a [null] event"
204                             + (it.hasNext() ? ": proceeding to the next action in the list"
205                                     : ": action list exhausted"));
206                 }
207                 eventIds[executionCount] = null;
208             }
209             executionCount++;
210         }
211         if (executionCount > 0) {
212             throw new NoMatchingTransitionException(getFlow().getId(), getId(), context.getLastEvent(),
213                     "No transition was matched on the event(s) signaled by the [" + executionCount
214                     + "] action(s) that executed in this action state '" + getId() + "' of flow '"
215                     + getFlow().getId() + "'; transitions must be defined to handle action result outcomes -- "
216                     + "possible flow configuration error? Note: the eventIds signaled were: '"
217                     + StylerUtils.style(eventIds)
218                     + "', while the supported set of transitional criteria for this action state is '"
219                     + StylerUtils.style(getTransitionSet().getTransitionCriterias()) + "'");
220         }
221         else {
222             throw new IllegalStateException JavaDoc(
223                     "No actions were executed, thus I cannot execute any state transition "
224                     + "-- programmer configuration error; make sure you add at least one action to this state's action list");
225         }
226     }
227
228     protected void appendToString(ToStringCreator creator) {
229         creator.append("actionList", actionList);
230         super.appendToString(creator);
231     }
232
233     /**
234      * Local "no transition found" exception used to report that an action
235      * result could not be mapped to a state transition.
236      *
237      * @author Keith Donald
238      * @author Erwin Vervaet
239      */

240     private static class NoMatchingActionResultTransitionException extends NoMatchingTransitionException {
241
242         /**
243          * Creates a new exception.
244          * @param state the action state
245          * @param resultEvent the action result event
246          */

247         public NoMatchingActionResultTransitionException(ActionState state, Event resultEvent) {
248             super(state.getFlow().getId(), state.getId(), resultEvent,
249                     "Cannot find a transition matching an action result event; continuing with next action...");
250         }
251     }
252 }
Popular Tags