From 3cf6ddfb61c16a0072b6c839e4ac57d2c9e63eff Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Mon, 31 Mar 2008 16:13:25 +0000 Subject: [PATCH] view-scope polishing --- .../webflow/engine/RequestControlContext.java | 10 -- .../webflow/engine/ViewState.java | 2 - .../engine/impl/FlowExecutionImpl.java | 3 + .../webflow/engine/impl/FlowSessionImpl.java | 30 ++++++ .../impl/RequestControlContextImpl.java | 19 +--- .../execution/FlowExecutionContext.java | 4 +- .../webflow/execution/FlowSession.java | 8 ++ .../webflow/test/MockFlowSession.java | 34 ++++++- .../execution/AbstractFlowExecutionTests.java | 98 ++++++++++++------- .../engine/impl/FlowExecutionImplTests.java | 23 +++++ .../test/SearchFlowExecutionTests.java | 8 +- 11 files changed, 164 insertions(+), 75 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 bea026ad..62a26df4 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 @@ -117,14 +117,4 @@ public interface RequestControlContext extends RequestContext { */ public boolean getAlwaysRedirectOnPause(); - /** - * Initialize view scope. Called by view states when they enter. - */ - public void initViewScope(); - - /** - * Destroy view-scope. Called by view-states when they exit. - */ - public void destroyViewScope(); - } \ No newline at end of file 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 0f170de1..c78f7708 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 @@ -162,7 +162,6 @@ public class ViewState extends TransitionableState { } protected void doPreEntryActions(RequestControlContext context) throws FlowExecutionException { - context.initViewScope(); createVariables(context); } @@ -203,7 +202,6 @@ public class ViewState extends TransitionableState { public void exit(RequestControlContext context) { destroyVariables(context); - context.destroyViewScope(); super.exit(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 ef5b5eb4..0a87b8d4 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 @@ -331,6 +331,9 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { void start(Flow flow, MutableAttributeMap input, RequestControlContext context) { listeners.fireSessionCreating(context, flow); FlowSession session = activateSession(flow); + if (input == null) { + input = new LocalAttributeMap(); + } listeners.fireSessionStarting(context, session, input); flow.start(context, input); listeners.fireSessionStarted(context, session); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java index 85e04763..d69357ed 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowSessionImpl.java @@ -40,6 +40,8 @@ import org.springframework.webflow.execution.FlowSession; */ class FlowSessionImpl implements FlowSession, Externalizable { + private static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap"; + /** * The flow definition (a singleton). *

@@ -104,6 +106,18 @@ class FlowSessionImpl implements FlowSession, Externalizable { return scope; } + public MutableAttributeMap getViewScope() throws IllegalStateException { + if (state == null) { + throw new IllegalStateException("The current state of this flow '" + flow.getId() + + "' is [null] - cannot access view scope"); + } + if (!state.isViewState()) { + throw new IllegalStateException("The current state '" + state.getId() + "' of this flow '" + flow.getId() + + "' is not a view state - view scope not accessible"); + } + return (MutableAttributeMap) scope.get(FLOW_VIEW_MAP_ATTRIBUTE); + } + public FlowSession getParent() { return parent; } @@ -158,8 +172,14 @@ class FlowSessionImpl implements FlowSession, Externalizable { Assert.notNull(state, "The state is required"); Assert.isTrue(flow == state.getOwner(), "The state does not belong to the flow associated with this flow session"); + if (this.state != null && this.state.isViewState()) { + destroyViewScope(); + } this.state = state; this.stateId = state.getId(); + if (this.state.isViewState()) { + initViewScope(); + } } /** @@ -176,6 +196,16 @@ class FlowSessionImpl implements FlowSession, Externalizable { return stateId; } + // internal helpers + + private void initViewScope() { + scope.put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap()); + } + + private void destroyViewScope() { + scope.remove(FLOW_VIEW_MAP_ATTRIBUTE); + } + public String toString() { return new ToStringCreator(this).append("flow", flowId).append("state", stateId).append("scope", scope) .toString(); 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 a5cd8275..a52fc9e3 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 @@ -48,8 +48,6 @@ import org.springframework.webflow.execution.FlowSession; */ class RequestControlContextImpl implements RequestControlContext { - private static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap"; - /** * The owning flow execution carrying out this request. */ @@ -123,14 +121,7 @@ class RequestControlContextImpl implements RequestControlContext { } public MutableAttributeMap getViewScope() throws IllegalStateException { - if (!flowExecution.isActive()) { - throw new IllegalStateException("This flow is not active"); - } - if (!getCurrentState().isViewState()) { - throw new IllegalStateException("The current state '" + getCurrentState().getId() + "' of this flow '" - + getActiveFlow().getId() + "' is not a view state - view scope not accessible"); - } - return (MutableAttributeMap) getFlowScope().get(FLOW_VIEW_MAP_ATTRIBUTE); + return flowExecution.getActiveSession().getViewScope(); } public MutableAttributeMap getFlowScope() { @@ -213,14 +204,6 @@ class RequestControlContextImpl implements RequestControlContext { flowExecution.start(flow, input, this); } - public void initViewScope() { - getFlowScope().put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap()); - } - - public void destroyViewScope() { - getFlowScope().remove(FLOW_VIEW_MAP_ATTRIBUTE); - } - public boolean handleEvent(Event event) throws FlowExecutionException { this.lastEvent = event; return flowExecution.handleEvent(event, this); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionContext.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionContext.java index 7b1a4236..86af89e2 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionContext.java @@ -100,8 +100,8 @@ public interface FlowExecutionContext { public FlowSession getActiveSession() throws IllegalStateException; /** - * Returns a mutable map for data held in "flash scope". Attributes in this map are cleared out on the next event - * signaled against this flow execution. Flash attributes survive flow execution refresh operations. + * Returns a mutable map for data held in "flash scope". Attributes in this map are cleared out on the next view + * rendering. Flash attributes survive flow execution refresh operations. * @return flash scope */ public MutableAttributeMap getFlashScope(); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowSession.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowSession.java index f3e0ef23..d235bf40 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowSession.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowSession.java @@ -54,6 +54,14 @@ public interface FlowSession { */ public MutableAttributeMap getScope(); + /** + * Returns a mutable map for data held in "view scope". Attributes in this map are cleared out when the current view + * state exits. + * @return view scope + * @throws IllegalStateException if this flow session is not currently in a view state + */ + public MutableAttributeMap getViewScope() throws IllegalStateException; + /** * Returns the parent flow session in the current flow execution, or null if there is no parent flow * session. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowSession.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowSession.java index cba37efd..1fbe3244 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowSession.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowSession.java @@ -36,14 +36,14 @@ import org.springframework.webflow.execution.FlowSession; */ public class MockFlowSession implements FlowSession { + private static final String FLOW_VIEW_MAP_ATTRIBUTE = "flowViewMap"; + private Flow definition; private State state; private MutableAttributeMap scope = new LocalAttributeMap(); - private MutableAttributeMap flashMap = new LocalAttributeMap(); - private FlowSession parent; /** @@ -90,8 +90,16 @@ public class MockFlowSession implements FlowSession { return scope; } - public MutableAttributeMap getFlashMap() { - return flashMap; + public MutableAttributeMap getViewScope() throws IllegalStateException { + if (state == null) { + throw new IllegalStateException("The current state of this flow '" + definition.getId() + + "' is [null] - cannot access view scope"); + } + if (!state.isViewState()) { + throw new IllegalStateException("The current state '" + state.getId() + "' of this flow '" + + definition.getId() + "' is not a view state - view scope not accessible"); + } + return (MutableAttributeMap) scope.get(FLOW_VIEW_MAP_ATTRIBUTE); } public FlowSession getParent() { @@ -115,7 +123,13 @@ public class MockFlowSession implements FlowSession { * Set the currently active state. */ public void setState(State state) { + if (this.state != null && this.state.isViewState()) { + destroyViewScope(); + } this.state = state; + if (this.state != null && this.state.isViewState()) { + initViewScope(); + } } /** @@ -133,7 +147,7 @@ public class MockFlowSession implements FlowSession { this.parent = parent; } - // conveniece accessors + // convenience accessors /** * Returns the flow definition of this session. @@ -148,4 +162,14 @@ public class MockFlowSession implements FlowSession { public State getStateInternal() { return state; } + + // internal helpers + + private void initViewScope() { + scope.put(FLOW_VIEW_MAP_ATTRIBUTE, new LocalAttributeMap()); + } + + private void destroyViewScope() { + scope.remove(FLOW_VIEW_MAP_ATTRIBUTE); + } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java b/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java index 0d13e049..2169e960 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java @@ -19,6 +19,7 @@ import junit.framework.TestCase; import org.springframework.util.Assert; import org.springframework.webflow.context.ExternalContext; +import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.engine.impl.FlowExecutionImplFactory; import org.springframework.webflow.execution.FlowExecution; @@ -33,9 +34,8 @@ import org.springframework.webflow.test.MockExternalContext; *

* More specifically, a typical flow execution test case will test: *