From 9f2b4081011506861df95cc853ec5e2682d5759f Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Fri, 11 Apr 2008 19:26:13 +0000 Subject: [PATCH] view rendering listener hooks --- .../webflow/engine/RequestControlContext.java | 31 +++++++++---- .../webflow/engine/ViewState.java | 2 + .../engine/impl/FlowExecutionImpl.java | 10 +++++ .../engine/impl/FlowExecutionListeners.java | 19 ++++++++ .../impl/RequestControlContextImpl.java | 45 +++++++++++-------- .../execution/FlowExecutionListener.java | 44 +++++++++--------- .../test/MockRequestControlContext.java | 25 +++++++---- .../builder/xml/XmlFlowModelBuilderTests.java | 16 +++++-- 8 files changed, 131 insertions(+), 61 deletions(-) diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java index d928b6fb..80851a55 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java @@ -22,6 +22,7 @@ import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionKey; import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; /** * Mutable control interface used to manipulate an ongoing flow execution in the context of one client request. @@ -60,19 +61,16 @@ public interface RequestControlContext extends RequestContext { public FlowExecutionKey assignFlowExecutionKey(); /** - * Update the current flow execution snapshot to save the current state. + * Called when a view is about to be rendered in the current view state. + * @param view the view to be rendered */ - public void updateCurrentFlowExecutionSnapshot(); + public void viewRendering(View view); /** - * Remove the current flow execution snapshot to invalidate the current state. + * Called when a view has completed rendering in the current view state. + * @param view the view that rendered */ - public void removeCurrentFlowExecutionSnapshot(); - - /** - * Remove all flow execution snapshots associated with the ongoing conversation. Invalidates previous states. - */ - public void removeAllFlowExecutionSnapshots(); + public void viewRendered(View view); /** * Signals the occurrence of an event in the current state of this flow execution request context. This method @@ -102,6 +100,21 @@ public interface RequestControlContext extends RequestContext { */ public void setCurrentTransition(Transition transition); + /** + * Update the current flow execution snapshot to save the current state. + */ + public void updateCurrentFlowExecutionSnapshot(); + + /** + * Remove the current flow execution snapshot to invalidate the current state. + */ + public void removeCurrentFlowExecutionSnapshot(); + + /** + * Remove all flow execution snapshots associated with the ongoing conversation. Invalidates previous states. + */ + public void removeAllFlowExecutionSnapshots(); + /** * Spawn a new flow session and activate it in the currently executing flow. Also transitions the spawned flow to * its start state. This method should be called by clients that wish to spawn new flows, such as subflow states. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java index 1d9c1ba2..cc07480f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java @@ -263,6 +263,7 @@ public class ViewState extends TransitionableState { logger.debug(" Flash scope = " + context.getFlashScope()); logger.debug(" Messages = " + context.getMessageContext()); } + context.viewRendering(view); renderActionList.execute(context); try { view.render(); @@ -271,6 +272,7 @@ public class ViewState extends TransitionableState { } context.getMessageContext().clearMessages(); context.getFlashScope().clear(); + context.viewRendered(view); } private void restoreVariables(RequestContext context) { 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 959d84eb..d50994d5 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 @@ -49,6 +49,7 @@ import org.springframework.webflow.execution.FlowExecutionListener; import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContextHolder; +import org.springframework.webflow.execution.View; /** * Default implementation of FlowExecution that uses a stack-based data structure to manage spawned flow sessions. This @@ -368,6 +369,14 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { listeners.fireStateEntered(context, previousState); } + public void viewRendering(View view, RequestContext context) { + listeners.fireViewRendering(context, view); + } + + public void viewRendered(View view, RequestContext context) { + listeners.fireViewRendered(context, view); + } + boolean handleEvent(Event event, RequestControlContext context) { listeners.fireEventSignaled(context, event); return getActiveSessionInternal().getFlow().handleEvent(context); @@ -637,4 +646,5 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { State currentState = (State) session.getState(); return currentState; } + } \ No newline at end of file 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 68b89298..c3d2d0d4 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 @@ -25,6 +25,7 @@ import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionListener; import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; /** * A helper that aids in publishing events to an array of FlowExecutionListener objects. @@ -153,6 +154,24 @@ class FlowExecutionListeners { } } + /** + * Notify all interested listeners that a flow execution view is rendering. + */ + public void fireViewRendering(RequestContext context, View view) { + for (int i = 0; i < listeners.length; i++) { + listeners[i].viewRendering(context, view, context.getCurrentState()); + } + } + + /** + * Notify all interested listeners that a flow execution has rendered. + */ + public void fireViewRendered(RequestContext context, View view) { + for (int i = 0; i < listeners.length; i++) { + listeners[i].viewRendered(context, view, context.getCurrentState()); + } + } + /** * Notify all interested listeners that a transition is being entered in the flow execution. */ diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java index 1ce4e502..eba71f2d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java @@ -35,6 +35,7 @@ import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionKey; import org.springframework.webflow.execution.FlowSession; +import org.springframework.webflow.execution.View; /** * Default request control context implementation used internally by the web flow system. This class is closely coupled @@ -187,14 +188,31 @@ class RequestControlContextImpl implements RequestControlContext { flowExecution.setCurrentState(state, this); } - public void setCurrentTransition(Transition transition) { - this.currentTransition = transition; - } - public FlowExecutionKey assignFlowExecutionKey() { return flowExecution.assignKey(); } + public void viewRendering(View view) { + flowExecution.viewRendering(view, this); + } + + public void viewRendered(View view) { + flowExecution.viewRendered(view, this); + } + + public boolean handleEvent(Event event) throws FlowExecutionException { + this.currentEvent = event; + return flowExecution.handleEvent(event, this); + } + + public boolean execute(Transition transition) { + return flowExecution.execute(transition, this); + } + + public void setCurrentTransition(Transition transition) { + this.currentTransition = transition; + } + public void updateCurrentFlowExecutionSnapshot() { flowExecution.updateCurrentFlowExecutionSnapshot(); } @@ -207,20 +225,6 @@ class RequestControlContextImpl implements RequestControlContext { flowExecution.removeAllFlowExecutionSnapshots(); } - public boolean getAlwaysRedirectOnPause() { - Boolean redirectOnPause = flowExecution.getAttributes().getBoolean("alwaysRedirectOnPause"); - return redirectOnPause != null ? redirectOnPause.booleanValue() : false; - } - - public boolean handleEvent(Event event) throws FlowExecutionException { - this.currentEvent = event; - return flowExecution.handleEvent(event, this); - } - - public boolean execute(Transition transition) { - return flowExecution.execute(transition, this); - } - public void start(Flow flow, MutableAttributeMap input) throws FlowExecutionException { flowExecution.start(flow, input, this); } @@ -229,6 +233,11 @@ class RequestControlContextImpl implements RequestControlContext { return flowExecution.endActiveFlowSession(output, this); } + public boolean getAlwaysRedirectOnPause() { + Boolean redirectOnPause = flowExecution.getAttributes().getBoolean("alwaysRedirectOnPause"); + return redirectOnPause != null ? redirectOnPause.booleanValue() : false; + } + public String toString() { return new ToStringCreator(this).append("externalContext", externalContext) .append("requestScope", requestScope).append("attributes", attributes).append("flowExecution", 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 54d79ec1..4ce38a7a 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 @@ -54,7 +54,7 @@ public interface FlowExecutionListener { /** * Called when any client request is submitted to manipulate this flow execution. This call happens before request * processing. - * @param context the source of the event + * @param context the current flow request context */ public void requestSubmitted(RequestContext context); @@ -68,7 +68,7 @@ public interface FlowExecutionListener { * Called to indicate a new flow definition session is about to be created. Called before the session is created. An * exception may be thrown from this method to veto the start operation. Any type of runtime exception can be used * for this purpose. - * @param context the source of the event + * @param context the current flow request context * @param definition the flow for which a new session is starting */ public void sessionCreating(RequestContext context, FlowDefinition definition); @@ -76,7 +76,7 @@ public interface FlowExecutionListener { /** * Called after a new flow session has been created but before it starts. Useful for setting arbitrary attributes in * the session before the flow starts. - * @param context the source of the event + * @param context the current flow request context * @param session the session that was created * @param input a mutable input map - attributes placed in this map are eligible for input mapping by the flow * definition at startup @@ -86,36 +86,44 @@ public interface FlowExecutionListener { /** * Called after a new flow session has started. At this point the flow's start state has been entered and any other * startup behaviors have been executed. - * @param context the source of the event + * @param context the current flow request context * @param session the session that was started */ public void sessionStarted(RequestContext context, FlowSession session); /** * Called when an event is signaled in the current state, but prior to any state transition. - * @param context the source of the event + * @param context the current flow request context * @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 context the current flow request context * @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 + * @param context the current flow request context * @param state the proposed state to transition to * @throws EnterStateVetoException when entering the state is not allowed */ public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException; + /** + * Called when a state transitions, after the transition occurred. + * @param context the current flow request context + * @param previousState from state of the transition + * @param state to state of the transition + */ + public void stateEntered(RequestContext context, StateDefinition previousState, StateDefinition state); + /** * Called when a view is about to render in a view-state, before any render actions are executed. - * @param context the current request context + * @param context the current flow request context * @param view the view that is about to render * @param viewState the current view state */ @@ -123,35 +131,27 @@ public interface FlowExecutionListener { /** * Called after a view has completed rendering. - * @param context the current request context + * @param context the current flow request context * @param view the view that rendered * @param viewState the current view state */ public void viewRendered(RequestContext context, View view, StateDefinition viewState); - /** - * 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 - */ - public void stateEntered(RequestContext context, StateDefinition previousState, StateDefinition state); - /** * Called when a flow execution is paused, for instance when it is waiting for user input (after event processing). - * @param context the source of the event + * @param context the current flow request context */ public void paused(RequestContext context); /** * Called after a flow execution is successfully reactivated after pause (but before event processing). - * @param context the source of the event + * @param context the current flow request context */ public void resuming(RequestContext context); /** * Called when the active flow execution session has been asked to end but before it has ended. - * @param context the source of the event + * @param context the current flow request context * @param session the current active session that is ending * @param output the flow output produced by the ending session, this map may be modified by this listener to affect * the output returned @@ -161,7 +161,7 @@ public interface FlowExecutionListener { /** * Called when a flow execution session ends. If the ended session was the root session of the flow execution, the * entire flow execution also ends. - * @param context the source of the event + * @param context the current flow request context * @param session ending flow session * @param output final, unmodifiable output returned by the ended session */ @@ -170,7 +170,7 @@ public interface FlowExecutionListener { /** * Called when an exception is thrown during a flow execution, before the exception is handled by any registered * {@link FlowExecutionExceptionHandler handler}. - * @param context the source of the exception + * @param context the current flow request context * @param exception the exception that occurred */ public void exceptionThrown(RequestContext context, FlowExecutionException exception); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java index 810d2cfd..f3c20753 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java @@ -25,6 +25,7 @@ import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowExecutionKey; import org.springframework.webflow.execution.FlowSession; +import org.springframework.webflow.execution.View; /** * Mock implementation of the {@link RequestControlContext} interface to facilitate standalone Flow and State unit @@ -76,6 +77,21 @@ public class MockRequestControlContext extends MockRequestContext implements Req return key; } + public void viewRendering(View view) { + } + + public void viewRendered(View view) { + } + + public boolean handleEvent(Event event) { + setCurrentEvent(event); + return ((Flow) getActiveFlow()).handleEvent(this); + } + + public boolean execute(Transition transition) { + return transition.execute((TransitionableState) getCurrentState(), this); + } + public void removeAllFlowExecutionSnapshots() { } @@ -88,15 +104,6 @@ public class MockRequestControlContext extends MockRequestContext implements Req } - public boolean handleEvent(Event event) { - setCurrentEvent(event); - return ((Flow) getActiveFlow()).handleEvent(this); - } - - public boolean execute(Transition transition) { - return transition.execute((TransitionableState) getCurrentState(), this); - } - public void start(Flow flow, MutableAttributeMap input) throws IllegalStateException { MockFlowSession session = new MockFlowSession(flow, input); if (getFlowExecutionContext().isActive()) { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java index fbee3558..0b54f4a0 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java @@ -278,9 +278,19 @@ public class XmlFlowModelBuilderTests extends TestCase { FlowExecutionImplFactory factory = new FlowExecutionImplFactory(); factory.setExecutionListenerLoader(new StaticFlowExecutionListenerLoader(new FlowExecutionListenerAdapter() { public void viewRendering(RequestContext context, View view, StateDefinition viewState) { - BindingResult result = (BindingResult) context.getFlashScope().get( - "org.springframework.validation.BindingResult.formBean"); - assertEquals(1, result.getErrorCount()); + if (context.getCurrentEvent() != null && context.getCurrentEvent().getId().equals("submit")) { + BindingResult result = (BindingResult) context.getFlashScope().get( + "org.springframework.validation.BindingResult.formBean"); + assertEquals(1, result.getErrorCount()); + } + } + + public void viewRendered(RequestContext context, View view, StateDefinition viewState) { + if (context.getCurrentEvent() != null && context.getCurrentEvent().getId().equals("submit")) { + BindingResult result = (BindingResult) context.getFlashScope().get( + "org.springframework.validation.BindingResult.formBean"); + assertNull(result); + } } })); FlowExecution execution = factory.createFlowExecution(flow);