KickJava   Java API By Example, From Geeks To Geeks.

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


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 org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.springframework.core.style.ToStringCreator;
21 import org.springframework.util.Assert;
22 import org.springframework.webflow.definition.TransitionDefinition;
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 path from one {@link TransitionableState state} to another
30  * {@link State state}.
31  * <p>
32  * When executed a transition takes a flow execution from its current state,
33  * called the <i>source state</i>, to another state, called the </i>target
34  * state</i>. A transition may become eligible for execution on the occurence
35  * of an {@link Event} from within a transitionable source state.
36  * <p>
37  * When an event occurs within this transition's source
38  * <code>TransitionableState</code> the determination of the eligibility of
39  * this transition is made by a <code>TransitionCriteria</code> object called
40  * the <i>matching criteria</i>. If the matching criteria returns
41  * <code>true</code> this transition is marked eligible for execution for that
42  * event.
43  * <p>
44  * Determination as to whether an eligible transition should be allowed to
45  * execute is made by a <code>TransitionCriteria</code> object called the
46  * <i>execution criteria</i>. If the execution criteria test fails this
47  * transition will <i>roll back</i> and reenter its source state. If the
48  * execution criteria test succeeds this transition will execute and take the
49  * flow to the transition's target state.
50  * <p>
51  * The target state of this transition is typically specified at configuration
52  * time in a static manner. If the target state of this transition needs to be
53  * calculated in a dynamic fashion at runtime configure a {@link TargetStateResolver}
54  * that supports such calculations.
55  *
56  * @see TransitionableState
57  * @see TransitionCriteria
58  * @see TargetStateResolver
59  *
60  * @author Keith Donald
61  * @author Erwin Vervaet
62  */

63 public class Transition extends AnnotatedObject implements TransitionDefinition {
64
65     /**
66      * Logger, for use in subclasses.
67      */

68     protected final Log logger = LogFactory.getLog(Transition.class);
69
70     /**
71      * The criteria that determine whether or not this transition matches as
72      * eligible for execution when an event occurs in the source state.
73      */

74     private TransitionCriteria matchingCriteria;
75
76     /**
77      * The criteria that determine whether or not this transition, once matched,
78      * should complete execution or should <i>roll back</i>.
79      */

80     private TransitionCriteria executionCriteria = WildcardTransitionCriteria.INSTANCE;
81
82     /**
83      * The resolver responsible for calculating the target state of this
84      * transition.
85      */

86     private TargetStateResolver targetStateResolver;
87
88     /**
89      * Create a new transition that always matches and always executes,
90      * transitioning to the target state calculated by the provided
91      * targetStateResolver.
92      * @param targetStateResolver the resolver of the target state of this
93      * transition
94      * @see #setMatchingCriteria(TransitionCriteria)
95      * @see #setExecutionCriteria(TransitionCriteria)
96      */

97     public Transition(TargetStateResolver targetStateResolver) {
98         this(WildcardTransitionCriteria.INSTANCE, targetStateResolver);
99     }
100
101     /**
102      * Create a new transition that matches on the specified criteria,
103      * transitioning to the target state calculated by the provided
104      * targetStateResolver.
105      * @param matchingCriteria the criteria for matching this transition
106      * @param targetStateResolver the resolver of the target state of this
107      * transition
108      * @see #setExecutionCriteria(TransitionCriteria)
109      */

110     public Transition(TransitionCriteria matchingCriteria, TargetStateResolver targetStateResolver) {
111         setMatchingCriteria(matchingCriteria);
112         setTargetStateResolver(targetStateResolver);
113     }
114
115     // implementing transition definition
116

117     public String JavaDoc getId() {
118         return matchingCriteria.toString();
119     }
120
121     public String JavaDoc getTargetStateId() {
122         return targetStateResolver.toString();
123     }
124
125     /**
126      * Returns the criteria that determine whether or not this transition
127      * matches as eligible for execution.
128      * @return the transition matching criteria
129      */

130     protected TransitionCriteria getMatchingCriteria() {
131         return matchingCriteria;
132     }
133
134     /**
135      * Set the criteria that determine whether or not this transition matches as
136      * eligible for execution.
137      * @param matchingCriteria the transition matching criteria
138      */

139     public void setMatchingCriteria(TransitionCriteria matchingCriteria) {
140         Assert.notNull(matchingCriteria, "The matching criteria is required");
141         this.matchingCriteria = matchingCriteria;
142     }
143
144     /**
145      * Returns the criteria that determine whether or not this transition, once
146      * matched, should complete execution or should <i>roll back</i>.
147      * @return the transition execution criteria
148      */

149     protected TransitionCriteria getExecutionCriteria() {
150         return executionCriteria;
151     }
152
153     /**
154      * Set the criteria that determine whether or not this transition, once
155      * matched, should complete execution or should <i>roll back</i>.
156      * @param executionCriteria the transition execution criteria
157      */

158     public void setExecutionCriteria(TransitionCriteria executionCriteria) {
159         Assert.notNull(executionCriteria, "The execution criteria is required");
160         this.executionCriteria = executionCriteria;
161     }
162
163     /**
164      * Returns this transition's target state resolver.
165      */

166     protected TargetStateResolver getTargetStateResolver() {
167         return targetStateResolver;
168     }
169
170     /**
171      * Set this transition's target state resolver, to calculate what state to
172      * transition to when this transition is executed.
173      * @param targetStateResolver the target state resolver
174      */

175     public void setTargetStateResolver(TargetStateResolver targetStateResolver) {
176         Assert.notNull(targetStateResolver, "The target state resolver is required");
177         this.targetStateResolver = targetStateResolver;
178     }
179
180     /**
181      * Checks if this transition is elligible for execution given the state of
182      * the provided flow execution request context.
183      * @param context the flow execution request context
184      * @return true if this transition should execute, false otherwise
185      */

186     public boolean matches(RequestContext context) {
187         return matchingCriteria.test(context);
188     }
189
190     /**
191      * Execute this state transition. Will only be called if the
192      * {@link #matches(RequestContext)} method returns true for given context.
193      * @param context the flow execution control context
194      * @return a view selection containing model and view information needed to
195      * render the results of the transition execution
196      * @throws FlowExecutionException when transition execution fails
197      */

198     public ViewSelection execute(State sourceState, RequestControlContext context) throws FlowExecutionException {
199         ViewSelection selectedView;
200         if (canExecute(context)) {
201             if (sourceState != null) {
202                 if (logger.isDebugEnabled()) {
203                     logger.debug("Executing " + this + " out of state '" + sourceState.getId() + "'");
204                 }
205                 if (sourceState instanceof TransitionableState) {
206                     // make exit call back on transitionable state
207
((TransitionableState)sourceState).exit(context);
208                 }
209             }
210             else {
211                 if (logger.isDebugEnabled()) {
212                     logger.debug("Executing " + this);
213                 }
214             }
215             State targetState = targetStateResolver.resolveTargetState(this, sourceState, context);
216             context.setLastTransition(this);
217             // enter the target state (note: any exceptions are propagated)
218
selectedView = targetState.enter(context);
219         }
220         else {
221             if (sourceState != null && sourceState instanceof TransitionableState) {
222                 // 'roll back' and re-enter the transitionable source state
223
selectedView = ((TransitionableState)sourceState).reenter(context);
224             }
225             else {
226                 throw new IllegalStateException JavaDoc(
227                         "Execution of '" + this + "' was blocked by '" + getExecutionCriteria()
228                         + "', " + "; however, no source state is set at runtime. "
229                         + "This is an illegal situation: check your flow definition.");
230             }
231         }
232         if (logger.isDebugEnabled()) {
233             if (context.getFlowExecutionContext().isActive()) {
234                 logger.debug("Completed execution of " + this + ", as a result the new state is '"
235                         + context.getCurrentState().getId() + "' in flow '" + context.getActiveFlow().getId() + "'");
236             }
237             else {
238                 logger.debug("Completed execution of " + this + ", as a result the flow execution has ended");
239             }
240         }
241         return selectedView;
242     }
243
244     /**
245      * Checks if this transition can complete its execution or should be rolled
246      * back, given the state of the flow execution request context.
247      * @param context the flow execution request context
248      * @return true if this transition can complete execution, false if it
249      * should roll back
250      */

251     protected boolean canExecute(RequestContext context) {
252         return executionCriteria.test(context);
253     }
254
255     public String JavaDoc toString() {
256         return new ToStringCreator(this).append("on", getMatchingCriteria()).append("to", getTargetStateResolver())
257                 .toString();
258     }
259 }
Popular Tags