added viewState method to allow stateful views to update their view scope state on view state exit

This commit is contained in:
Keith Donald
2009-04-16 21:57:22 +00:00
parent 21a04ee02f
commit 8f7e3ade44
11 changed files with 93 additions and 13 deletions

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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() {

View File

@@ -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

View File

@@ -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() {

View File

@@ -59,5 +59,9 @@ public class StubViewFactory implements ViewFactory {
return new Event(this, context.getExternalContext().getRequestParameterMap().get("_eventId"));
}
public void saveState() {
}
}
}

View File

@@ -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();