From 8f7e3ade447ec536f6bae86cd3c5f1f256e1b952 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Thu, 16 Apr 2009 21:57:22 +0000 Subject: [PATCH] added viewState method to allow stateful views to update their view scope state on view state exit --- .../webflow/engine/RequestControlContext.java | 10 ++++++++-- .../webflow/engine/ViewState.java | 7 +++++++ .../impl/RequestControlContextImpl.java | 13 +++++++++++++ .../support/ActionExecutingViewFactory.java | 8 ++++++-- .../webflow/execution/RequestContext.java | 8 ++++++++ .../webflow/execution/View.java | 9 +++++++++ .../webflow/mvc/view/AbstractMvcView.java | 8 ++++++-- .../webflow/test/MockRequestContext.java | 19 +++++++++++++++++-- .../webflow/test/MockViewFactoryCreator.java | 14 +++++++++----- .../webflow/engine/StubViewFactory.java | 4 ++++ .../engine/model/AbstractModelTests.java | 6 ++++++ 11 files changed, 93 insertions(+), 13 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 24712df7..f12f0194 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 @@ -60,13 +60,19 @@ public interface RequestControlContext extends RequestContext { public FlowExecutionKey assignFlowExecutionKey(); /** - * Called when a view is about to be rendered in the current view state. + * Sets the current view. + * @param view the current view, or null to mark the current view as null + */ + public void setCurrentView(View view); + + /** + * Called when the current view is about to be rendered in the current view state. * @param view the view to be rendered */ public void viewRendering(View view); /** - * Called when a view has completed rendering in the current view state. + * Called when the current view has completed rendering in the current view state. * @param view the view that rendered */ public void viewRendered(View view); 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 9a129504..a7063ed2 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 @@ -182,6 +182,7 @@ public class ViewState extends TransitionableState { } } else { View view = viewFactory.getView(context); + context.setCurrentView(view); render(context, view); } } @@ -190,6 +191,7 @@ public class ViewState extends TransitionableState { public void resume(RequestControlContext context) { restoreVariables(context); View view = viewFactory.getView(context); + context.setCurrentView(view); if (view.userEventQueued()) { boolean stateExited = handleEvent(view, context); if (!stateExited) { @@ -244,6 +246,7 @@ public class ViewState extends TransitionableState { super.exit(context); updateHistory(context); destroyVariables(context); + context.setCurrentView(null); } // internal helpers @@ -306,6 +309,10 @@ public class ViewState extends TransitionableState { TransitionDefinition transition = context.getCurrentTransition(); History history = (History) transition.getAttributes().get("history"); if (history == null || history == History.PRESERVE) { + View currentView = context.getCurrentView(); + if (currentView != null) { + currentView.saveState(); + } context.updateCurrentFlowExecutionSnapshot(); } else if (history == History.DISCARD) { context.removeCurrentFlowExecutionSnapshot(); 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 52ff65ed..260612f7 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 @@ -81,6 +81,11 @@ class RequestControlContextImpl implements RequestControlContext { */ private Transition currentTransition; + /** + * The current view associated with this request context; initially null. + */ + private View currentView; + /** * Create a new request context. * @param flowExecution the owning flow execution @@ -157,6 +162,10 @@ class RequestControlContextImpl implements RequestControlContext { return currentTransition; } + public View getCurrentView() { + return currentView; + } + public MutableAttributeMap getAttributes() { return attributes; } @@ -184,6 +193,10 @@ class RequestControlContextImpl implements RequestControlContext { return flowExecution.assignKey(); } + public void setCurrentView(View currentView) { + this.currentView = currentView; + } + public void viewRendering(View view) { flowExecution.viewRendering(view, this); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java index 5ced9fc5..90868705 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/support/ActionExecutingViewFactory.java @@ -79,6 +79,10 @@ public class ActionExecutingViewFactory implements ViewFactory { userEventProcessed = true; } + public Object getUserEventState() { + return null; + } + public boolean hasFlowEvent() { return userEventProcessed && getEventId() != null; } @@ -90,8 +94,8 @@ public class ActionExecutingViewFactory implements ViewFactory { return new Event(this, eventId); } - public Object getUserEventState() { - return null; + public void saveState() { + } private String getEventId() { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/RequestContext.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/RequestContext.java index 269c646d..2110ed38 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/RequestContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/RequestContext.java @@ -185,6 +185,14 @@ public interface RequestContext { */ public TransitionDefinition getCurrentTransition(); + /** + * Returns the current view in use; if not null, the view returned is about to be rendered, is rendering, is + * processing a user event, or has finished user event processing and the current ViewState is exiting due to a + * state transition. Returns null if the flow is not in a view state. + * @return the current view, or null if the flow is not in a view state + */ + public View getCurrentView(); + /** * Returns a context map for accessing attributes about the state of the current request. These attributes may be * used to influence flow execution behavior. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java index 622dc44c..8176358b 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/View.java @@ -17,6 +17,8 @@ package org.springframework.webflow.execution; import java.io.IOException; +import org.springframework.webflow.engine.ViewState; + /** * Allows a client to participate in flow execution. Encapsulates behavior to send the client an appropriate response * and handle the resulting event once the client responds. @@ -77,4 +79,11 @@ public interface View { */ public Object getUserEventState(); + /** + * Saves any state associated with this view out to view scope. Called when exiting a {@link ViewState} to allow for + * any changes applied after postback processing to be captured and reflected when going back. Can be a no-op for + * views that store no view state. + */ + public void saveState(); + } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java index 1af0ddce..6278718d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java @@ -224,6 +224,10 @@ public abstract class AbstractMvcView implements View { userEventProcessed = true; } + public Object getUserEventState() { + return new ViewActionStateHolder(eventId, userEventProcessed, mappingResults); + } + public boolean hasFlowEvent() { return userEventProcessed && !requestContext.getMessageContext().hasErrorMessages(); } @@ -235,8 +239,8 @@ public abstract class AbstractMvcView implements View { return new Event(this, getEventId(), requestContext.getRequestParameters().asAttributeMap()); } - public Object getUserEventState() { - return new ViewActionStateHolder(eventId, userEventProcessed, mappingResults); + public void saveState() { + } public String toString() { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestContext.java index 2ae48dec..962b5079 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestContext.java @@ -33,10 +33,11 @@ import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; /** - * Mock implementation of the RequestContext interface to facilitate standalone flow artifact (e.g. - * action) unit tests. + * Mock implementation of the RequestContext interface to facilitate standalone flow artifact (e.g. action) + * unit tests. * * @see org.springframework.webflow.execution.RequestContext * @see org.springframework.webflow.execution.Action @@ -60,6 +61,8 @@ public class MockRequestContext implements RequestContext { private Transition currentTransition; + private View currentView; + /** * Convenience constructor that creates a new mock request context with the following defaults: *