From 295f608663a07b27be783d28eb9d0d903f1320eb Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Thu, 28 Feb 2008 18:30:11 +0000 Subject: [PATCH] OPEN - issue SWF-487: Add transitionExecuting flow execution listener hook. http://jira.springframework.org/browse/SWF-487 --- .../engine/impl/FlowExecutionImpl.java | 1 + .../engine/impl/FlowExecutionListeners.java | 11 ++++++++ .../execution/FlowExecutionListener.java | 13 ++++++++-- .../FlowExecutionListenerAdapter.java | 5 ++++ .../engine/impl/FlowExecutionImplTests.java | 26 +++++++++++++++++++ .../execution/MockFlowExecutionListener.java | 16 ++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) 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;