diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
index 25836e1c..b6cfedfc 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
@@ -350,6 +350,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
}
void execute(Transition transition, RequestControlContext context) {
+ listeners.fireTransitionExecuting(context, transition);
transition.execute(getCurrentState(), context);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java
index d0035ee9..68b89298 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java
@@ -19,6 +19,7 @@ import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
+import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionListener;
@@ -32,6 +33,7 @@ import org.springframework.webflow.execution.RequestContext;
*
* @author Keith Donald
* @author Erwin Vervaet
+ * @author Scott Andrews
*/
class FlowExecutionListeners {
@@ -151,6 +153,15 @@ class FlowExecutionListeners {
}
}
+ /**
+ * Notify all interested listeners that a transition is being entered in the flow execution.
+ */
+ public void fireTransitionExecuting(RequestContext context, TransitionDefinition transition) {
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].transitionExecuting(context, transition);
+ }
+ }
+
/**
* Notify all interested listeners that a flow session was paused in the flow execution.
*/
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java
index 1aa7315c..24e3e7c9 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java
@@ -19,6 +19,7 @@ import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
+import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
/**
@@ -46,6 +47,7 @@ import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
*
* @author Keith Donald
* @author Erwin Vervaet
+ * @author Scott Andrews
*/
public interface FlowExecutionListener {
@@ -92,10 +94,17 @@ public interface FlowExecutionListener {
/**
* Called when an event is signaled in the current state, but prior to any state transition.
* @param context the source of the event
- * @param event the event that occured
+ * @param event the event that occurred
*/
public void eventSignaled(RequestContext context, Event event);
+ /**
+ * Called when a transition is matched but before the transition occurs.
+ * @param context the source of the event
+ * @param transition the proposed transition
+ */
+ public void transitionExecuting(RequestContext context, TransitionDefinition transition);
+
/**
* Called when a state transitions, after the transition is matched but before the transition occurs.
* @param context the source of the event
@@ -105,7 +114,7 @@ public interface FlowExecutionListener {
public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException;
/**
- * Called when a state transitions, after the transition occured.
+ * Called when a state transitions, after the transition occurred.
* @param context the source of the event
* @param previousState from state of the transition
* @param state to state of the transition
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java
index dd9f1077..c86a164c 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java
@@ -19,6 +19,7 @@ import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
+import org.springframework.webflow.definition.TransitionDefinition;
/**
* An abstract adapter class for listeners (observers) of flow execution lifecycle events. The methods in this class are
@@ -26,6 +27,7 @@ import org.springframework.webflow.definition.StateDefinition;
*
* @author Erwin Vervaet
* @author Keith Donald
+ * @author Scott Andrews
*/
public abstract class FlowExecutionListenerAdapter implements FlowExecutionListener {
@@ -47,6 +49,9 @@ public abstract class FlowExecutionListenerAdapter implements FlowExecutionListe
public void eventSignaled(RequestContext context, Event event) {
}
+ public void transitionExecuting(RequestContext context, TransitionDefinition transition) {
+ }
+
public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException {
}
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java
index f6f1a1d2..673659b1 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java
@@ -25,7 +25,9 @@ import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.RequestControlContext;
import org.springframework.webflow.engine.State;
import org.springframework.webflow.engine.StubViewFactory;
+import org.springframework.webflow.engine.Transition;
import org.springframework.webflow.engine.ViewState;
+import org.springframework.webflow.engine.support.DefaultTargetStateResolver;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionKey;
@@ -72,6 +74,7 @@ public class FlowExecutionImplTests extends TestCase {
assertEquals(1, mockListener.getSessionEndingCount());
assertEquals(1, mockListener.getSessionEndedCount());
assertEquals(0, mockListener.getEventSignaledCount());
+ assertEquals(0, mockListener.getTransitionExecutingCount());
assertEquals(0, mockListener.getPausedCount());
assertEquals(0, mockListener.getResumingCount());
assertEquals(0, mockListener.getExceptionThrownCount());
@@ -282,6 +285,29 @@ public class FlowExecutionImplTests extends TestCase {
}
}
+ public void testExecuteTransition() {
+ Flow flow = new Flow("flow");
+ ViewState state = new ViewState(flow, "view", new StubViewFactory()) {
+ public void resume(RequestControlContext context) {
+ context.execute(getRequiredTransition(context));
+ }
+ };
+ state.getTransitionSet().add(new Transition(new DefaultTargetStateResolver("finish")));
+ EndState end = new EndState(flow, "finish");
+ MockFlowExecutionListener mockListener = new MockFlowExecutionListener();
+ FlowExecutionListener[] listeners = new FlowExecutionListener[] { mockListener };
+ FlowExecutionImpl execution = new FlowExecutionImpl(flow);
+ execution.setListeners(listeners);
+ execution.setMessageContextFactory(new DefaultMessageContextFactory(new StaticMessageSource()));
+ execution.setKeyFactory(new MockFlowExecutionKeyFactory());
+ MockExternalContext context = new MockExternalContext();
+ execution.start(null, context);
+ assertEquals(0, mockListener.getTransitionExecutingCount());
+ execution.resume(context);
+ assertTrue(execution.hasEnded());
+ assertEquals(1, mockListener.getTransitionExecutingCount());
+ }
+
public void testRequestContextManagedOnStartAndResume() {
Flow flow = new Flow("flow");
new ViewState(flow, "view", new StubViewFactory()) {
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java b/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java
index 5ddcd2f7..a4d47510 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java
@@ -20,12 +20,14 @@ import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
+import org.springframework.webflow.definition.TransitionDefinition;
/**
* Mock implementation of the FlowExecutionListener interface for use in unit tests.
*
* @author Keith Donald
* @author Erwin Vervaet
+ * @author Scott Andrews
*/
public class MockFlowExecutionListener extends FlowExecutionListenerAdapter {
@@ -45,6 +47,8 @@ public class MockFlowExecutionListener extends FlowExecutionListenerAdapter {
private int stateEnteredCount;
+ private int transitionExecutingCount;
+
private int resumingCount;
private boolean paused;
@@ -157,6 +161,13 @@ public class MockFlowExecutionListener extends FlowExecutionListenerAdapter {
return stateEnteredCount;
}
+ /**
+ * Returns the number of transitions entered so far.
+ */
+ public int getTransitionExecutingCount() {
+ return transitionExecutingCount;
+ }
+
/**
* Returns the number of events signaled so far.
*/
@@ -246,6 +257,10 @@ public class MockFlowExecutionListener extends FlowExecutionListenerAdapter {
stateEnteredCount++;
}
+ public void transitionExecuting(RequestContext context, TransitionDefinition transition) {
+ transitionExecutingCount++;
+ }
+
public void paused(RequestContext context) {
executing = false;
paused = true;
@@ -302,6 +317,7 @@ public class MockFlowExecutionListener extends FlowExecutionListenerAdapter {
sessionStartedCount = 0;
stateEnteringCount = 0;
stateEnteredCount = 0;
+ transitionExecutingCount = 0;
eventSignaledCount = 0;
pausedCount = 0;
resumingCount = 0;