added viewState method to allow stateful views to update their view scope state on view state exit
This commit is contained in:
@@ -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 <code>null</code>
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 <code>null</code> if the flow is not in a view state.
|
||||
* @return the current view, or <code>null</code> 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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 <code>RequestContext</code> interface to facilitate standalone flow artifact (e.g.
|
||||
* action) unit tests.
|
||||
* Mock implementation of the <code>RequestContext</code> 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:
|
||||
* <ul>
|
||||
@@ -176,6 +179,10 @@ public class MockRequestContext implements RequestContext {
|
||||
return currentTransition;
|
||||
}
|
||||
|
||||
public View getCurrentView() {
|
||||
return currentView;
|
||||
}
|
||||
|
||||
public MutableAttributeMap getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
@@ -240,6 +247,14 @@ public class MockRequestContext implements RequestContext {
|
||||
this.currentTransition = transition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current view in this request context.
|
||||
* @param currentView the current view
|
||||
*/
|
||||
public void setCurrentView(View currentView) {
|
||||
this.currentView = currentView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a request context attribute.
|
||||
* @param attributeName the attribute name
|
||||
|
||||
@@ -95,6 +95,10 @@ class MockViewFactoryCreator implements ViewFactoryCreator {
|
||||
return viewId;
|
||||
}
|
||||
|
||||
public void render() throws IOException {
|
||||
context.getExternalContext().getResponseWriter().write(viewId);
|
||||
}
|
||||
|
||||
public boolean userEventQueued() {
|
||||
return hasFlowEvent();
|
||||
}
|
||||
@@ -103,6 +107,10 @@ class MockViewFactoryCreator implements ViewFactoryCreator {
|
||||
// TODO - implement me as appropriate for a test environment
|
||||
}
|
||||
|
||||
public Object getUserEventState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasFlowEvent() {
|
||||
return context.getRequestParameters().contains("_eventId");
|
||||
}
|
||||
@@ -111,12 +119,8 @@ class MockViewFactoryCreator implements ViewFactoryCreator {
|
||||
return new Event(this, context.getRequestParameters().get("_eventId"));
|
||||
}
|
||||
|
||||
public Object getUserEventState() {
|
||||
return null;
|
||||
}
|
||||
public void saveState() {
|
||||
|
||||
public void render() throws IOException {
|
||||
context.getExternalContext().getResponseWriter().write(viewId);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -59,5 +59,9 @@ public class StubViewFactory implements ViewFactory {
|
||||
return new Event(this, context.getExternalContext().getRequestParameterMap().get("_eventId"));
|
||||
}
|
||||
|
||||
public void saveState() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,12 @@ public class AbstractModelTests extends TestCase {
|
||||
assertEquals(null, obj.merge(child, parent));
|
||||
}
|
||||
|
||||
public void testMergeModel() {
|
||||
AttributeModel child = new AttributeModel("foo", "bar");
|
||||
AttributeModel parent = new AttributeModel("foo", "baz");
|
||||
|
||||
}
|
||||
|
||||
public void testListMerge() {
|
||||
AbstractModel obj = new PersistenceContextModel();
|
||||
LinkedList child = new LinkedList();
|
||||
|
||||
Reference in New Issue
Block a user