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:
*