From 5486b2c8d7ab9b00e5897cd7140205930e37f324 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Tue, 22 Apr 2008 22:13:04 +0000 Subject: [PATCH] flow outcome modeling flow possible outcomes --- .../webflow/definition/FlowDefinition.java | 6 +++ .../webflow/engine/EndState.java | 6 +-- .../springframework/webflow/engine/Flow.java | 33 ++++++++++--- .../webflow/engine/RequestControlContext.java | 9 ++-- .../engine/impl/FlowExecutionImpl.java | 28 +++++++---- .../engine/impl/FlowExecutionListeners.java | 9 ++-- .../impl/RequestControlContextImpl.java | 5 +- .../execution/FlowExecutionContext.java | 6 +-- .../execution/FlowExecutionListener.java | 8 +-- .../FlowExecutionListenerAdapter.java | 4 +- .../execution/FlowExecutionOutcome.java | 49 +++++++++++++++++++ .../webflow/executor/FlowExecutionResult.java | 31 ++++-------- .../mvc/portlet/AbstractFlowHandler.java | 4 +- .../webflow/mvc/portlet/FlowHandler.java | 5 +- .../mvc/portlet/FlowHandlerAdapter.java | 12 ++--- .../mvc/servlet/AbstractFlowHandler.java | 4 +- .../webflow/mvc/servlet/FlowController.java | 18 +++---- .../webflow/mvc/servlet/FlowHandler.java | 5 +- .../mvc/servlet/FlowHandlerAdapter.java | 10 ++-- .../HibernateFlowExecutionListener.java | 2 +- .../persistence/JpaFlowExecutionListener.java | 2 +- .../SecurityFlowExecutionListener.java | 42 ++++++---------- .../test/MockFlowExecutionContext.java | 21 +++++--- .../test/MockRequestControlContext.java | 9 ++-- .../execution/AbstractFlowExecutionTests.java | 6 +-- .../FlowDefinitionRegistryImplTests.java | 8 +++ .../webflow/engine/FlowTests.java | 12 ++++- .../model/FlowModelFlowBuilderTests.java | 16 +++--- .../execution/MockFlowExecutionListener.java | 4 +- .../executor/FlowExecutorImplTests.java | 20 ++++---- .../mvc/portlet/FlowHandlerAdapterTests.java | 8 ++- .../mvc/servlet/FlowControllerTests.java | 21 ++++---- .../mvc/servlet/FlowHandlerAdapterTests.java | 13 +++-- .../HibernateFlowExecutionListenerTests.java | 8 +-- .../JpaFlowExecutionListenerTests.java | 8 +-- 35 files changed, 260 insertions(+), 192 deletions(-) create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionOutcome.java diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/FlowDefinition.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/FlowDefinition.java index 3037e07b..d44fbab1 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/definition/FlowDefinition.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/FlowDefinition.java @@ -63,6 +63,12 @@ public interface FlowDefinition extends Annotated { */ public StateDefinition getState(String id) throws IllegalArgumentException; + /** + * Returns the outcomes that are possible for this flow to reach. + * @return the possible outcomes + */ + public String[] getPossibleOutcomes(); + /** * Returns a reference to application context hosting application objects and services needed by this flow * definition. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java index e9376ed4..01ad73cc 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/EndState.java @@ -20,7 +20,6 @@ import org.springframework.binding.mapping.MappingResults; import org.springframework.core.style.ToStringCreator; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.execution.Action; -import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; @@ -99,12 +98,11 @@ public class EndState extends State { if (finalResponseAction != null && context.getExternalContext().isResponseAllowed()) { ActionExecutor.execute(finalResponseAction, context); } - context.endActiveFlowSession(createSessionOutput(context)); + context.endActiveFlowSession(getId(), createSessionOutput(context)); } else { // there is a parent flow that will resume (this flow is a subflow) LocalAttributeMap sessionOutput = createSessionOutput(context); - context.endActiveFlowSession(sessionOutput); - context.handleEvent(new Event(this, getId(), sessionOutput)); + context.endActiveFlowSession(getId(), sessionOutput); } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java index 33cadc3d..4c35471c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java @@ -15,9 +15,11 @@ */ package org.springframework.webflow.engine; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -76,16 +78,17 @@ import org.springframework.webflow.execution.RequestContext; * implementations but may also be directly instantiated. *

* This class and the rest of the Spring Web Flow (SWF) engine have been designed with minimal dependencies on other - * libraries. Spring Web Flow is usable in a standalone fashion (as well as in the context of other frameworks like - * Spring MVC, Struts, or JSF, for example). The engine system is fully usable outside an HTTP servlet environment, for - * example in portlets, tests, or standalone applications. One of the major architectural benefits of Spring Web Flow is - * the ability to design reusable, high-level controller modules that may be executed in any environment. + * libraries. Spring Web Flow is usable in a standalone fashion. The engine system is fully usable outside an HTTP + * servlet environment, for example in portlets, tests, or standalone applications. One of the major architectural + * benefits of Spring Web Flow is the ability to design reusable, high-level controller modules that may be executed in + * any environment. *

* Note: flows are singleton definition objects so they should be thread-safe. You can think a flow definition as * analogous to a Java class, defining all the behavior of an application module. The core behaviors * {@link #start(RequestControlContext, MutableAttributeMap) start}, {@link #resume(RequestControlContext)}, - * {@link #handleEvent(RequestControlContext) on event}, {@link #end(RequestControlContext, MutableAttributeMap) end}, - * and {@link #handleException(FlowExecutionException, RequestControlContext)}. Each method accepts a + * {@link #handleEvent(RequestControlContext) on event}, + * {@link #end(RequestControlContext, String, MutableAttributeMap) end}, and + * {@link #handleException(FlowExecutionException, RequestControlContext)}. Each method accepts a * {@link RequestContext request context} that allows for this flow to access execution state in a thread safe manner. A * flow execution is what models a running instance of this flow definition, somewhat analogous to a java object that is * an instance of a class. @@ -210,6 +213,17 @@ public class Flow extends AnnotatedObject implements FlowDefinition { return getStateInstance(stateId); } + public String[] getPossibleOutcomes() { + List possibleOutcomes = new ArrayList(); + for (Iterator it = states.iterator(); it.hasNext();) { + State state = (State) it.next(); + if (state instanceof EndState) { + possibleOutcomes.add(state.getId()); + } + } + return (String[]) possibleOutcomes.toArray(new String[possibleOutcomes.size()]); + } + public ApplicationContext getApplicationContext() { return applicationContext; } @@ -541,10 +555,13 @@ public class Flow extends AnnotatedObject implements FlowDefinition { * mapper ({@link #setOutputMapper(Mapper)}). * * @param context the flow execution control context + * @param outcome the logical flow outcome that will be returned by the session, generally the id of the terminating + * end state * @param output initial output produced by the session that is eligible for modification by this method * @throws FlowExecutionException when an exception occurs ending this flow */ - public void end(RequestControlContext context, MutableAttributeMap output) throws FlowExecutionException { + public void end(RequestControlContext context, String outcome, MutableAttributeMap output) + throws FlowExecutionException { endActionList.execute(context); if (outputMapper != null) { MappingResults results = outputMapper.map(context, output); @@ -584,7 +601,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition { } } - private void restoreVariables(RequestContext context) { + public void restoreVariables(RequestContext context) { Iterator it = variables.values().iterator(); while (it.hasNext()) { FlowVariable variable = (FlowVariable) it.next(); 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 4c1dfca7..dd17b8a9 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 @@ -20,7 +20,6 @@ import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionKey; -import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.View; @@ -132,12 +131,12 @@ public interface RequestControlContext extends RequestContext { * End the active flow session of the current flow execution. This method should be called by clients that terminate * flows, such as end states. The end() method of the flow involved in the flow execution will be * called. - * @param output output produced by the session that is eligible for mapping by a resuming parent flow - * @return the ended session + * @param outcome the logical outcome the ending session should return + * @param output output the ending session should return * @throws IllegalStateException when the flow execution is not active - * @see Flow#end(RequestControlContext, MutableAttributeMap) + * @see Flow#end(RequestControlContext, String, MutableAttributeMap) */ - public FlowSession endActiveFlowSession(MutableAttributeMap output) throws IllegalStateException; + public void endActiveFlowSession(String outcome, MutableAttributeMap output) throws IllegalStateException; /** * Returns true if the 'always redirect pause' flow execution attribute is set to true, false otherwise. 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 ada08bc8..c74ac485 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 @@ -46,6 +46,7 @@ import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionKey; import org.springframework.webflow.execution.FlowExecutionKeyFactory; import org.springframework.webflow.execution.FlowExecutionListener; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContextHolder; @@ -134,9 +135,9 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { private String flowId; /** - * The flow execution outcome event. + * The outcome reached by this flow execution when it ends. */ - private transient Event outcome; + private transient FlowExecutionOutcome outcome; /** * Default constructor required for externalizable serialization. Should NOT be called programmatically. @@ -195,7 +196,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { return hasStarted() && !isActive(); } - public Event getOutcome() { + public FlowExecutionOutcome getOutcome() { return outcome; } @@ -383,16 +384,23 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { return transition.execute(getCurrentState(), context); } - FlowSession endActiveFlowSession(MutableAttributeMap output, RequestControlContext context) { + void endActiveFlowSession(String outcome, MutableAttributeMap output, RequestControlContext context) { FlowSessionImpl session = getActiveSessionInternal(); - listeners.fireSessionEnding(context, session, output); - session.getFlow().end(context, output); + listeners.fireSessionEnding(context, session, outcome, output); + session.getFlow().end(context, outcome, output); flowSessions.removeLast(); - listeners.fireSessionEnded(context, session, output); - if (hasEnded()) { - outcome = new Event(this, session.getState().getId(), output); + boolean executionEnded = hasEnded(); + if (executionEnded) { + // set the root flow execution outcome for external clients to use + this.outcome = new FlowExecutionOutcome(outcome, output); + } + listeners.fireSessionEnded(context, session, outcome, output); + if (!executionEnded) { + // restore any variables that may have transient references + getActiveSessionInternal().getFlow().restoreVariables(context); + // treat the outcome as an event against the current state of the new active flow + context.handleEvent(new Event(session.getState(), outcome, output)); } - return session; } FlowExecutionKey assignKey() { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java index 8e2959c8..1e23443d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionListeners.java @@ -202,18 +202,19 @@ class FlowExecutionListeners { /** * Notify all interested listeners that the active flow execution session is ending. */ - public void fireSessionEnding(RequestContext context, FlowSession session, MutableAttributeMap output) { + public void fireSessionEnding(RequestContext context, FlowSession session, String outcomeId, + MutableAttributeMap output) { for (int i = 0; i < listeners.length; i++) { - listeners[i].sessionEnding(context, session, output); + listeners[i].sessionEnding(context, session, outcomeId, output); } } /** * Notify all interested listeners that a flow execution session has ended. */ - public void fireSessionEnded(RequestContext context, FlowSession session, AttributeMap output) { + public void fireSessionEnded(RequestContext context, FlowSession session, String outcomeId, AttributeMap output) { for (int i = 0; i < listeners.length; i++) { - listeners[i].sessionEnded(context, session, output); + listeners[i].sessionEnded(context, session, outcomeId, output); } } 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 b2aaf1e8..93882d40 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 @@ -32,7 +32,6 @@ import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionKey; -import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.View; /** @@ -218,8 +217,8 @@ class RequestControlContextImpl implements RequestControlContext { flowExecution.start(flow, input, this); } - public FlowSession endActiveFlowSession(MutableAttributeMap output) throws IllegalStateException { - return flowExecution.endActiveFlowSession(output, this); + public void endActiveFlowSession(String outcome, MutableAttributeMap output) throws IllegalStateException { + flowExecution.endActiveFlowSession(outcome, output, this); } public boolean getAlwaysRedirectOnPause() { 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 1eeb9a79..5a5f43e9 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 @@ -84,10 +84,10 @@ public interface FlowExecutionContext { public boolean hasEnded(); /** - * Returns the ending outcome event of this execution, or null if this execution has not yet ended. - * @return the outcome event + * Returns the outcome reached by this execution, or null if this execution has not yet ended. + * @return the outcome, or null if this execution has not yet ended */ - public Event getOutcome(); + public FlowExecutionOutcome getOutcome(); /** * Returns the active flow session of this flow execution. The active flow session is the currently executing diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java index 1c0eead6..89eb22a5 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListener.java @@ -153,19 +153,21 @@ public interface FlowExecutionListener { * Called when the active flow execution session has been asked to end but before it has ended. * @param context the current flow request context * @param session the current active session that is ending + * @param outcome the outcome reached by the ending session, generally the id of the terminating end-state * @param output the flow output produced by the ending session, this map may be modified by this listener to affect * the output returned */ - public void sessionEnding(RequestContext context, FlowSession session, MutableAttributeMap output); + public void sessionEnding(RequestContext context, FlowSession session, String outcome, MutableAttributeMap output); /** * Called when a flow execution session ends. If the ended session was the root session of the flow execution, the * entire flow execution also ends. * @param context the current flow request context * @param session ending flow session - * @param output final, unmodifiable output returned by the ended session + * @param outcome the outcome reached by the ended session, generally the id of the terminating end-state + * @param output the flow output returned by the ending session */ - public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output); + public void sessionEnded(RequestContext context, FlowSession session, String outcome, AttributeMap output); /** * Called when an exception is thrown during a flow execution, before the exception is handled by any registered diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java index 82779b8e..cd2fb63f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionListenerAdapter.java @@ -70,10 +70,10 @@ public abstract class FlowExecutionListenerAdapter implements FlowExecutionListe public void resuming(RequestContext context) { } - public void sessionEnding(RequestContext context, FlowSession session, MutableAttributeMap output) { + public void sessionEnding(RequestContext context, FlowSession session, String outcome, MutableAttributeMap output) { } - public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) { + public void sessionEnded(RequestContext context, FlowSession session, String outcome, AttributeMap output) { } public void exceptionThrown(RequestContext context, FlowExecutionException exception) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionOutcome.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionOutcome.java new file mode 100644 index 00000000..3c4e4ad4 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionOutcome.java @@ -0,0 +1,49 @@ +package org.springframework.webflow.execution; + +import org.springframework.core.style.ToStringCreator; +import org.springframework.webflow.core.collection.AttributeMap; +import org.springframework.webflow.core.collection.CollectionUtils; + +/** + * An outcome returned by a flow execution when it ends. + * + * @author Keith Donald + */ +public class FlowExecutionOutcome { + + private String id; + + private AttributeMap output; + + /** + * Creates a new flow execution outcome + * @param id the outcome id + * @param output the output returned by the execution + */ + public FlowExecutionOutcome(String id, AttributeMap output) { + super(); + this.id = id; + this.output = (output != null ? output : CollectionUtils.EMPTY_ATTRIBUTE_MAP); + } + + /** + * Return the outcome identifier. + * @return the outcome that was reached by the flow execution + */ + public String getId() { + return id; + } + + /** + * Returns the output returned by the flow execution. + * @return the output that was returned + */ + public AttributeMap getOutput() { + return output; + } + + public String toString() { + return new ToStringCreator(this).append("id", id).append("output", output).toString(); + } + +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutionResult.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutionResult.java index 53b60b8a..e91c6cf1 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutionResult.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutionResult.java @@ -15,8 +15,7 @@ */ package org.springframework.webflow.executor; -import org.springframework.webflow.core.collection.AttributeMap; -import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.FlowExecutionOutcome; /** * A value object providing information about the result of a flow execution request. @@ -29,15 +28,12 @@ public class FlowExecutionResult { private String flowExecutionKey; - private String outcome; + private FlowExecutionOutcome outcome; - private AttributeMap output; - - private FlowExecutionResult(String flowId, String flowExecutionKey, String outcome, AttributeMap output) { + private FlowExecutionResult(String flowId, String flowExecutionKey, FlowExecutionOutcome outcome) { this.flowId = flowId; this.flowExecutionKey = flowExecutionKey; this.outcome = outcome; - this.output = output; } /** @@ -48,7 +44,7 @@ public class FlowExecutionResult { * @return the result */ public static FlowExecutionResult createPausedResult(String flowId, String flowExecutionKey) { - return new FlowExecutionResult(flowId, flowExecutionKey, null, null); + return new FlowExecutionResult(flowId, flowExecutionKey, null); } /** @@ -57,8 +53,8 @@ public class FlowExecutionResult { * @param outcome the ending execution outcome * @return the result */ - public static FlowExecutionResult createEndedResult(String flowId, Event outcome) { - return new FlowExecutionResult(flowId, null, outcome.getId(), outcome.getAttributes()); + public static FlowExecutionResult createEndedResult(String flowId, FlowExecutionOutcome outcome) { + return new FlowExecutionResult(flowId, null, outcome); } /** @@ -95,21 +91,12 @@ public class FlowExecutionResult { } /** - * Returns the flow execution outcome when ab ended result. + * Returns the flow execution outcome when an ended result. * @see #ended() - * @return the ended outcome + * @return the ended outcome, or null if this is not an ended result */ - public String getEndedOutcome() { + public FlowExecutionOutcome getOutcome() { return outcome; } - /** - * Returns the output returned from the flow execution when an ended result. - * @see #ended() - * @return the ended output - */ - public AttributeMap getEndedOutput() { - return output; - } - } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java index 8c6e1e05..036500fc 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java @@ -21,8 +21,8 @@ import javax.portlet.RenderResponse; import org.springframework.web.portlet.ModelAndView; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; /** * Trivial flow handler base class that simply returns null for all operations. Subclasses should extend and override @@ -44,7 +44,7 @@ public class AbstractFlowHandler implements FlowHandler { return null; } - public String handleFlowOutcome(String outcome, AttributeMap output, RenderRequest request, RenderResponse response) { + public String handleFlowOutcome(FlowExecutionOutcome outcome, RenderRequest request, RenderResponse response) { return null; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java index b844fbc2..894cea50 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java @@ -21,8 +21,8 @@ import javax.portlet.RenderResponse; import org.springframework.web.portlet.ModelAndView; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; /** * A controller helper used for customizing access to a single flow definition. This helper is used to: @@ -55,13 +55,12 @@ public interface FlowHandler { /** * Handles a specific flow execution outcome. Used to select a new view to render after the flow ends. * @param outcome the outcome that was reached - * @param output the output returned by the flow execution * @param request the current render request * @param response the current render response * @return the id of the flow to start after handling the outcome, or null if the outcome should be handled by the * caller */ - public String handleFlowOutcome(String outcome, AttributeMap output, RenderRequest request, RenderResponse response); + public String handleFlowOutcome(FlowExecutionOutcome outcome, RenderRequest request, RenderResponse response); /** * Handles a flow exception that was not handled by the Web Flow system. Used by a Controller to handle a specific diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java index c9e39929..e6b24b33 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java @@ -34,9 +34,9 @@ import org.springframework.webflow.context.portlet.DefaultFlowUrlHandler; import org.springframework.webflow.context.portlet.FlowUrlHandler; import org.springframework.webflow.context.portlet.PortletExternalContext; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; @@ -88,10 +88,8 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen .getAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE); if (result != null) { session.removeAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE); - String outcome = result.getEndedOutcome(); - AttributeMap output = result.getEndedOutput(); - String flowId = flowHandler.handleFlowOutcome(outcome, output, request, response); - return defaultHandleFlowOutcome(flowHandler, outcome, output, flowId, request, response); + String flowId = flowHandler.handleFlowOutcome(result.getOutcome(), request, response); + return defaultHandleFlowOutcome(flowHandler, result.getOutcome(), flowId, request, response); } else { return startFlow(request, response, flowHandler); } @@ -145,7 +143,7 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen return inputMap; } - protected ModelAndView defaultHandleFlowOutcome(FlowHandler flowHandler, String outcome, AttributeMap output, + protected ModelAndView defaultHandleFlowOutcome(FlowHandler flowHandler, FlowExecutionOutcome outcome, String nextFlowId, RenderRequest request, RenderResponse response) throws IOException { if (nextFlowId == null) { nextFlowId = flowHandler.getFlowId(); @@ -154,7 +152,7 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen logger.debug("Starting a new execution of flow '" + nextFlowId + "'"); } PortletExternalContext context = createPortletExternalContext(request, response); - flowExecutor.launchExecution(nextFlowId, new LocalAttributeMap(output.asMap()), context); + flowExecutor.launchExecution(nextFlowId, new LocalAttributeMap(outcome.getOutput().asMap()), context); return null; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java index de84d312..51e8f7a6 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java @@ -19,8 +19,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; /** * Trivial flow handler base class that simply returns null for all operations. Subclasses should extend and override @@ -38,7 +38,7 @@ public class AbstractFlowHandler implements FlowHandler { return null; } - public String handleExecutionOutcome(String outcome, AttributeMap output, HttpServletRequest request, + public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { return null; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java index 7b0bd585..e86bbe0c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java @@ -35,6 +35,7 @@ import org.springframework.webflow.core.FlowException; import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; @@ -195,14 +196,14 @@ public class FlowController extends AbstractController { return inputMap; } - protected ModelAndView defaultHandleFlowOutcome(String flowId, String outcome, AttributeMap endedOutput, + protected ModelAndView defaultHandleFlowOutcome(String flowId, FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) throws IOException { if (!response.isCommitted()) { // by default, just start the flow over passing the output as input if (logger.isDebugEnabled()) { logger.debug("Restarting a new execution of ended flow '" + flowId + "'"); } - response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, endedOutput, request)); + response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, outcome.getOutput(), request)); } return null; } @@ -269,8 +270,7 @@ public class FlowController extends AbstractController { sendRedirect(context, request, response, context.getExternalRedirectUrl()); return null; } else { - return handleFlowOutcome(result.getFlowId(), result.getEndedOutcome(), result.getEndedOutput(), - request, response); + return handleFlowOutcome(result.getFlowId(), result.getOutcome(), request, response); } } else { throw new IllegalStateException("Execution result should have been one of [paused] or [ended]"); @@ -295,15 +295,15 @@ public class FlowController extends AbstractController { } } - private ModelAndView handleFlowOutcome(String flowId, String outcome, AttributeMap endedOutput, - HttpServletRequest request, HttpServletResponse response) throws IOException { + private ModelAndView handleFlowOutcome(String flowId, FlowExecutionOutcome outcome, HttpServletRequest request, + HttpServletResponse response) throws IOException { FlowHandler handler = getFlowHandler(flowId); if (handler != null) { - String location = handler.handleExecutionOutcome(outcome, endedOutput, request, response); + String location = handler.handleExecutionOutcome(outcome, request, response); return location != null ? createRedirectView(location, request) : defaultHandleFlowOutcome(flowId, outcome, - endedOutput, request, response); + request, response); } else { - return defaultHandleFlowOutcome(flowId, outcome, endedOutput, request, response); + return defaultHandleFlowOutcome(flowId, outcome, request, response); } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java index ebb5da76..82911d9c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java @@ -19,8 +19,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; /** * A controller helper used for customizing access to a single flow definition. This helper is used to: @@ -59,13 +59,12 @@ public interface FlowHandler { * to the current servlet path. * * @param outcome the outcome that was reached - * @param output the output returned by the flow execution * @param request the current request * @param response the current response * @return the location of the new resource to redirect to, or null if the execution outcome was not handled and * should be handled by the caller */ - public String handleExecutionOutcome(String outcome, AttributeMap output, HttpServletRequest request, + public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response); /** diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java index 90a3afc9..80c12707 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java @@ -35,6 +35,7 @@ import org.springframework.webflow.core.FlowException; import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; @@ -159,14 +160,14 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H return inputMap; } - protected ModelAndView defaultHandleFlowOutcome(String flowId, String outcome, AttributeMap endedOutput, + protected ModelAndView defaultHandleFlowOutcome(String flowId, FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) throws IOException { if (!response.isCommitted()) { // by default, just start the flow over passing the output as input if (logger.isDebugEnabled()) { logger.debug("Restarting a new execution of ended flow '" + flowId + "'"); } - response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, endedOutput, request)); + response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, outcome.getOutput(), request)); } return null; } @@ -225,10 +226,9 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H sendRedirect(context, request, response, context.getExternalRedirectUrl()); return null; } else { - String location = handler.handleExecutionOutcome(result.getEndedOutcome(), result.getEndedOutput(), - request, response); + String location = handler.handleExecutionOutcome(result.getOutcome(), request, response); return location != null ? createRedirectView(location, request) : defaultHandleFlowOutcome(result - .getFlowId(), result.getEndedOutcome(), result.getEndedOutput(), request, response); + .getFlowId(), result.getOutcome(), request, response); } } else { throw new IllegalStateException("Execution result should have been one of [paused] or [ended]"); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/persistence/HibernateFlowExecutionListener.java b/spring-webflow/src/main/java/org/springframework/webflow/persistence/HibernateFlowExecutionListener.java index 0302064d..b848bfcf 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/persistence/HibernateFlowExecutionListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/persistence/HibernateFlowExecutionListener.java @@ -131,7 +131,7 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter } } - public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) { + public void sessionEnded(RequestContext context, FlowSession session, String outcome, AttributeMap output) { if (isPersistenceContext(session.getDefinition())) { final Session hibernateSession = (Session) session.getScope().remove(HIBERNATE_SESSION_ATTRIBUTE); Boolean commitStatus = session.getState().getAttributes().getBoolean("commit"); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/persistence/JpaFlowExecutionListener.java b/spring-webflow/src/main/java/org/springframework/webflow/persistence/JpaFlowExecutionListener.java index 34ca0b9b..69c34804 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/persistence/JpaFlowExecutionListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/persistence/JpaFlowExecutionListener.java @@ -119,7 +119,7 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter { } } - public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) { + public void sessionEnded(RequestContext context, FlowSession session, String outcome, AttributeMap output) { if (isPersistenceContext(session.getDefinition())) { final EntityManager em = (EntityManager) session.getScope().remove(ENTITY_MANAGER_ATTRIBUTE); Boolean commitStatus = session.getState().getAttributes().getBoolean("commit"); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/security/SecurityFlowExecutionListener.java b/spring-webflow/src/main/java/org/springframework/webflow/security/SecurityFlowExecutionListener.java index 05416202..536b8569 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/security/SecurityFlowExecutionListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/security/SecurityFlowExecutionListener.java @@ -42,11 +42,24 @@ import org.springframework.webflow.execution.RequestContext; */ public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter { - AccessDecisionManager accessDecisionManager; + private AccessDecisionManager accessDecisionManager; /** - * Check security authorization when flow session starts + * Get the access decision manager that makes flow authorization decisions. + * @return the decision manager */ + public AccessDecisionManager getAccessDecisionManager() { + return accessDecisionManager; + } + + /** + * Set the access decision manager that makes flow authorization decisions. + * @param accessDecisionManager the decision manager to user + */ + public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) { + this.accessDecisionManager = accessDecisionManager; + } + public void sessionCreating(RequestContext context, FlowDefinition definition) { SecurityRule rule = (SecurityRule) definition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME); if (rule != null) { @@ -54,9 +67,6 @@ public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter } } - /** - * Check security authorization when entering state - */ public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException { SecurityRule rule = (SecurityRule) state.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME); if (rule != null) { @@ -64,9 +74,6 @@ public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter } } - /** - * Check security authorization on transition - */ public void transitionExecuting(RequestContext context, TransitionDefinition transition) { SecurityRule rule = (SecurityRule) transition.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME); if (rule != null) { @@ -81,7 +88,7 @@ public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter * @param rule the rule to base the decision * @param object the execution listener phase */ - public void decide(SecurityRule rule, Object object) { + protected void decide(SecurityRule rule, Object object) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); ConfigAttributeDefinition config = new ConfigAttributeDefinition(getConfigAttributes(rule)); if (accessDecisionManager != null) { @@ -115,21 +122,4 @@ public class SecurityFlowExecutionListener extends FlowExecutionListenerAdapter } return configAttributes; } - - /** - * Get decision manager - * @return the decision manager - */ - public AccessDecisionManager getAccessDecisionManager() { - return accessDecisionManager; - } - - /** - * Set decision manager - * @param accessDecisionManager the decision manager to user - */ - public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) { - this.accessDecisionManager = accessDecisionManager; - } - } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionContext.java index 79250b47..ed3bd793 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionContext.java @@ -20,9 +20,9 @@ import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.engine.Flow; -import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowExecutionKey; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.FlowSession; /** @@ -34,6 +34,8 @@ import org.springframework.webflow.execution.FlowSession; */ public class MockFlowExecutionContext implements FlowExecutionContext { + private boolean started; + private FlowExecutionKey key; private FlowDefinition flow; @@ -46,14 +48,14 @@ public class MockFlowExecutionContext implements FlowExecutionContext { private MutableAttributeMap attributes = new LocalAttributeMap(); - private Event outcome; + private FlowExecutionOutcome outcome; /** * Creates a new mock flow execution context -- automatically installs a root flow definition and active flow * session. */ public MockFlowExecutionContext() { - activeSession = new MockFlowSession(); + setActiveSession(new MockFlowSession()); this.flow = activeSession.getDefinition(); } @@ -68,8 +70,8 @@ public class MockFlowExecutionContext implements FlowExecutionContext { * Creates a new mock flow execution context for the specified active flow session. */ public MockFlowExecutionContext(FlowSession flowSession) { + setActiveSession(flowSession); this.flow = flowSession.getDefinition(); - this.activeSession = flowSession; } public FlowExecutionKey getKey() { @@ -87,7 +89,7 @@ public class MockFlowExecutionContext implements FlowExecutionContext { } public boolean hasStarted() { - return isActive(); + return started; } public boolean isActive() { @@ -117,7 +119,7 @@ public class MockFlowExecutionContext implements FlowExecutionContext { return attributes; } - public Event getOutcome() { + public FlowExecutionOutcome getOutcome() { return outcome; } @@ -148,6 +150,9 @@ public class MockFlowExecutionContext implements FlowExecutionContext { */ public void setActiveSession(FlowSession activeSession) { this.activeSession = activeSession; + if (!started && activeSession != null) { + started = true; + } } /** @@ -166,9 +171,9 @@ public class MockFlowExecutionContext implements FlowExecutionContext { /** * Sets the result of this flow ending. - * @param outcome the ending outcome event. + * @param outcome the ended outcome */ - public void setOutcome(Event outcome) { + public void setOutcome(FlowExecutionOutcome outcome) { this.outcome = outcome; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java index 1dcb8871..02bf84b9 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java @@ -24,7 +24,6 @@ import org.springframework.webflow.engine.TransitionableState; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecutionContext; import org.springframework.webflow.execution.FlowExecutionKey; -import org.springframework.webflow.execution.FlowSession; import org.springframework.webflow.execution.View; /** @@ -113,11 +112,13 @@ public class MockRequestControlContext extends MockRequestContext implements Req flow.start(this, input); } - public FlowSession endActiveFlowSession(MutableAttributeMap output) throws IllegalStateException { + public void endActiveFlowSession(String outcome, MutableAttributeMap output) throws IllegalStateException { MockFlowSession endingSession = getMockFlowExecutionContext().getMockActiveSession(); - endingSession.getDefinitionInternal().end(this, output); + endingSession.getDefinitionInternal().end(this, outcome, output); getMockFlowExecutionContext().setActiveSession(endingSession.getParent()); - return endingSession; + if (!getMockFlowExecutionContext().hasEnded()) { + handleEvent(new Event(endingSession.getState(), outcome, output)); + } } public boolean getAlwaysRedirectOnPause() { 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 ad7ff07d..cb1997f3 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 @@ -23,10 +23,10 @@ import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.engine.impl.FlowExecutionImpl; import org.springframework.webflow.engine.impl.FlowExecutionImplFactory; -import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecution; import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionFactory; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.test.MockExternalContext; /** @@ -70,7 +70,7 @@ public abstract class AbstractFlowExecutionTests extends TestCase { /** * The outcome that was reached when the flow ends; initially null. */ - private Event flowExecutionOutcome; + private FlowExecutionOutcome flowExecutionOutcome; /** * Constructs a default flow execution test. @@ -168,7 +168,7 @@ public abstract class AbstractFlowExecutionTests extends TestCase { * Returns the flow execution outcome that was reached. * @return the flow execution outcome, or null if the flow execution has not ended */ - protected Event getFlowExecutionOutcome() { + protected FlowExecutionOutcome getFlowExecutionOutcome() { return flowExecutionOutcome; } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java b/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java index 4318a295..38a787e3 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistryImplTests.java @@ -110,6 +110,10 @@ public class FlowDefinitionRegistryImplTests extends TestCase { return null; } + public String[] getPossibleOutcomes() { + return null; + } + public ApplicationContext getApplicationContext() { return null; } @@ -143,6 +147,10 @@ public class FlowDefinitionRegistryImplTests extends TestCase { return null; } + public String[] getPossibleOutcomes() { + return null; + } + public ApplicationContext getApplicationContext() { return null; } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java index 8534798f..b11651eb 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java @@ -144,6 +144,14 @@ public class FlowTests extends TestCase { } } + public void testGetPossibleOutcomes() { + Flow flow = new Flow("myFlow"); + new EndState(flow, "myState1"); + new EndState(flow, "myState2"); + assertEquals("myState1", flow.getPossibleOutcomes()[0]); + assertEquals("myState2", flow.getPossibleOutcomes()[1]); + } + public void testAddActions() { flow.getStartActionList().add(new TestMultiAction()); flow.getStartActionList().add(new TestMultiAction()); @@ -299,7 +307,7 @@ public class FlowTests extends TestCase { flow.getEndActionList().add(action); MockRequestControlContext context = new MockRequestControlContext(flow); LocalAttributeMap sessionOutput = new LocalAttributeMap(); - flow.end(context, sessionOutput); + flow.end(context, "finish", sessionOutput); assertEquals(1, action.getExecutionCount()); } @@ -314,7 +322,7 @@ public class FlowTests extends TestCase { MockRequestControlContext context = new MockRequestControlContext(flow); context.getFlowScope().put("attr", "foo"); LocalAttributeMap sessionOutput = new LocalAttributeMap(); - flow.end(context, sessionOutput); + flow.end(context, "finish", sessionOutput); assertEquals("foo", sessionOutput.get("attr")); } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java index 68ce7b4d..f634d92d 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java @@ -35,9 +35,9 @@ import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder; import org.springframework.webflow.engine.model.registry.FlowModelHolder; import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl; import org.springframework.webflow.engine.support.ActionExecutingViewFactory; -import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecution; import org.springframework.webflow.execution.FlowExecutionException; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.ViewFactory; import org.springframework.webflow.security.SecurityRule; import org.springframework.webflow.test.MockExternalContext; @@ -137,14 +137,14 @@ public class FlowModelFlowBuilderTests extends TestCase { map.put("number", "3"); map.put("required", "9"); execution.start(map, context); - Event outcome = execution.getOutcome(); + FlowExecutionOutcome outcome = execution.getOutcome(); assertEquals("end", outcome.getId()); - assertEquals("bar", outcome.getAttributes().get("foo")); - assertEquals("bar", outcome.getAttributes().get("differentName")); - assertEquals(new Integer(3), outcome.getAttributes().get("number")); - assertEquals(new Integer(3), outcome.getAttributes().get("required")); - assertEquals("a literal", outcome.getAttributes().get("literal")); - assertNull(outcome.getAttributes().get("notReached")); + assertEquals("bar", outcome.getOutput().get("foo")); + assertEquals("bar", outcome.getOutput().get("differentName")); + assertEquals(new Integer(3), outcome.getOutput().get("number")); + assertEquals(new Integer(3), outcome.getOutput().get("required")); + assertEquals("a literal", outcome.getOutput().get("literal")); + assertNull(outcome.getOutput().get("notReached")); } public void testFlowRequiredInputMapping() { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java b/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java index 18d6979e..c9cabd96 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/execution/MockFlowExecutionListener.java @@ -273,13 +273,13 @@ public class MockFlowExecutionListener extends FlowExecutionListenerAdapter { resumingCount++; } - public void sessionEnding(RequestContext context, FlowSession session, MutableAttributeMap output) { + public void sessionEnding(RequestContext context, FlowSession session, String outcome, MutableAttributeMap output) { sessionEnding = true; sessionEndingCount++; flowNestingLevel--; } - public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) { + public void sessionEnded(RequestContext context, FlowSession session, String outcome, AttributeMap output) { assertStarted(); Assert.state(sessionEnding, "Should have been ending"); sessionEnding = false; diff --git a/spring-webflow/src/test/java/org/springframework/webflow/executor/FlowExecutorImplTests.java b/spring-webflow/src/test/java/org/springframework/webflow/executor/FlowExecutorImplTests.java index 1a593b83..cfc63efe 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/executor/FlowExecutorImplTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/executor/FlowExecutorImplTests.java @@ -8,10 +8,10 @@ import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.definition.registry.FlowDefinitionLocator; -import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecution; import org.springframework.webflow.execution.FlowExecutionFactory; import org.springframework.webflow.execution.FlowExecutionKey; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.FlowExecutionLock; import org.springframework.webflow.execution.repository.FlowExecutionRepository; import org.springframework.webflow.test.GeneratedFlowExecutionKey; @@ -65,8 +65,7 @@ public class FlowExecutorImplTests extends TestCase { assertTrue(result.paused()); assertEquals("12345", result.getPausedKey()); assertFalse(result.ended()); - assertNull(result.getEndedOutcome()); - assertNull(result.getEndedOutput()); + assertNull(result.getOutcome()); assertNull(ExternalContextHolder.getExternalContext()); verifyMocks(); } @@ -86,14 +85,14 @@ public class FlowExecutorImplTests extends TestCase { EasyMock.expect(execution.getDefinition()).andReturn(definition); EasyMock.expect(definition.getId()).andReturn("foo"); - EasyMock.expect(execution.getOutcome()).andReturn(new Event(execution, "finish", null)); + EasyMock.expect(execution.getOutcome()).andReturn(new FlowExecutionOutcome("finish", null)); replayMocks(); FlowExecutionResult result = flowExecutor.launchExecution("foo", null, context); assertTrue(result.ended()); - assertEquals("finish", result.getEndedOutcome()); - assertTrue(result.getEndedOutput().isEmpty()); + assertEquals("finish", result.getOutcome().getId()); + assertTrue(result.getOutcome().getOutput().isEmpty()); assertFalse(result.paused()); assertNull(result.getPausedKey()); assertNull(ExternalContextHolder.getExternalContext()); @@ -131,8 +130,7 @@ public class FlowExecutorImplTests extends TestCase { assertTrue(result.paused()); assertEquals("12345", result.getPausedKey()); assertFalse(result.ended()); - assertNull(result.getEndedOutcome()); - assertNull(result.getEndedOutput()); + assertNull(result.getOutcome()); assertNull(ExternalContextHolder.getExternalContext()); verifyMocks(); @@ -159,7 +157,7 @@ public class FlowExecutorImplTests extends TestCase { LocalAttributeMap output = new LocalAttributeMap(); output.put("foo", "bar"); - EasyMock.expect(execution.getOutcome()).andReturn(new Event(execution, "finish", output)); + EasyMock.expect(execution.getOutcome()).andReturn(new FlowExecutionOutcome("finish", output)); repository.removeFlowExecution(execution); @@ -169,8 +167,8 @@ public class FlowExecutorImplTests extends TestCase { FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context); assertTrue(result.ended()); - assertEquals("finish", result.getEndedOutcome()); - assertEquals(output, result.getEndedOutput()); + assertEquals("finish", result.getOutcome().getId()); + assertEquals(output, result.getOutcome().getOutput()); assertFalse(result.paused()); assertNull(result.getPausedKey()); assertNull(ExternalContextHolder.getExternalContext()); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java index 85f5e469..29b166f2 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java @@ -20,10 +20,9 @@ import org.springframework.web.portlet.ModelAndView; import org.springframework.webflow.context.portlet.DefaultFlowUrlHandler; import org.springframework.webflow.context.portlet.PortletExternalContext; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; -import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; @@ -85,8 +84,7 @@ public class FlowHandlerAdapterTests extends TestCase { } } - public String handleFlowOutcome(String outcome, AttributeMap output, RenderRequest request, - RenderResponse response) { + public String handleFlowOutcome(FlowExecutionOutcome outcome, RenderRequest request, RenderResponse response) { if (handleExecutionOutcome) { return "home"; } else { @@ -174,7 +172,7 @@ public class FlowHandlerAdapterTests extends TestCase { renderRequest.setContextPath("/springtravel"); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); PortletSession session = renderRequest.getPortletSession(); session.setAttribute("flowExecutionResult", result); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java index 4f46e0ad..d14b86f1 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java @@ -16,10 +16,9 @@ import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.ModelAndView; import org.springframework.webflow.context.servlet.ServletExternalContext; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; -import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; @@ -77,7 +76,7 @@ public class FlowControllerTests extends TestCase { executor.launchExecution("foo", new LocalAttributeMap(parameters), context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -117,7 +116,7 @@ public class FlowControllerTests extends TestCase { executor.resumeExecution("12345", context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -207,7 +206,7 @@ public class FlowControllerTests extends TestCase { executor.launchExecution("foo", new LocalAttributeMap(parameters), context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -289,7 +288,7 @@ public class FlowControllerTests extends TestCase { return input; } - public String handleExecutionOutcome(String outcome, AttributeMap output, HttpServletRequest request, + public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { return null; } @@ -324,10 +323,10 @@ public class FlowControllerTests extends TestCase { return input; } - public String handleExecutionOutcome(String outcome, AttributeMap output, HttpServletRequest request, + public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { - assertEquals("finish", outcome); - assertEquals("baz", output.get("bar")); + assertEquals("finish", outcome.getId()); + assertEquals("baz", outcome.getOutput().get("bar")); assertEquals(FlowControllerTests.this.request, request); assertEquals(FlowControllerTests.this.response, response); return null; @@ -345,7 +344,7 @@ public class FlowControllerTests extends TestCase { executor.launchExecution("foo", input, context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -368,7 +367,7 @@ public class FlowControllerTests extends TestCase { return null; } - public String handleExecutionOutcome(String outcome, AttributeMap output, HttpServletRequest request, + public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { return null; } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java index 575ba092..9fc1f948 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java @@ -16,10 +16,9 @@ import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.ModelAndView; import org.springframework.webflow.context.servlet.ServletExternalContext; import org.springframework.webflow.core.FlowException; -import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; -import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; @@ -61,7 +60,7 @@ public class FlowHandlerAdapterTests extends TestCase { return "foo"; } - public String handleExecutionOutcome(String outcome, AttributeMap output, HttpServletRequest request, + public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { if (handleExecutionOutcome) { return "/home"; @@ -107,7 +106,7 @@ public class FlowHandlerAdapterTests extends TestCase { executor.launchExecution("foo", flowInput, context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -147,7 +146,7 @@ public class FlowHandlerAdapterTests extends TestCase { executor.resumeExecution("12345", context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -191,7 +190,7 @@ public class FlowHandlerAdapterTests extends TestCase { executor.launchExecution("foo", flowInput, context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); @@ -271,7 +270,7 @@ public class FlowHandlerAdapterTests extends TestCase { executor.launchExecution("foo", flowInput, context); LocalAttributeMap output = new LocalAttributeMap(); output.put("bar", "baz"); - Event outcome = new Event(this, "finish", output); + FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output); FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); EasyMock.expectLastCall().andReturn(result); EasyMock.replay(new Object[] { executor }); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/persistence/HibernateFlowExecutionListenerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/persistence/HibernateFlowExecutionListenerTests.java index be3a9566..9fb06d47 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/persistence/HibernateFlowExecutionListenerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/persistence/HibernateFlowExecutionListenerTests.java @@ -118,7 +118,7 @@ public class HibernateFlowExecutionListenerTests extends TestCase { endState.getAttributes().put("commit", Boolean.TRUE); flowSession.setState(endState); - hibernateListener.sessionEnded(context, flowSession, null); + hibernateListener.sessionEnded(context, flowSession, "success", null); assertEquals("Table should only have two rows", 2, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertSessionNotBound(); assertFalse(flowSession.getScope().contains("hibernate.session")); @@ -149,7 +149,7 @@ public class HibernateFlowExecutionListenerTests extends TestCase { endState.getAttributes().put("commit", Boolean.TRUE); flowSession.setState(endState); - hibernateListener.sessionEnded(context, flowSession, null); + hibernateListener.sessionEnded(context, flowSession, "success", null); assertEquals("Table should only have three rows", 3, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertFalse(flowSession.getScope().contains("hibernate.session")); @@ -174,7 +174,7 @@ public class HibernateFlowExecutionListenerTests extends TestCase { EndState endState = new EndState(flowSession.getDefinitionInternal(), "cancel"); endState.getAttributes().put("commit", Boolean.FALSE); flowSession.setState(endState); - hibernateListener.sessionEnded(context, flowSession, null); + hibernateListener.sessionEnded(context, flowSession, "cancel", null); assertEquals("Table should only have two rows", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertSessionNotBound(); assertFalse(flowSession.getScope().contains("hibernate.session")); @@ -192,7 +192,7 @@ public class HibernateFlowExecutionListenerTests extends TestCase { EndState endState = new EndState(flowSession.getDefinitionInternal(), "cancel"); flowSession.setState(endState); - hibernateListener.sessionEnded(context, flowSession, null); + hibernateListener.sessionEnded(context, flowSession, "cancel", null); assertEquals("Table should only have three rows", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertFalse(flowSession.getScope().contains("hibernate.session")); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/persistence/JpaFlowExecutionListenerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/persistence/JpaFlowExecutionListenerTests.java index 710b1fb3..2f8a5734 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/persistence/JpaFlowExecutionListenerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/persistence/JpaFlowExecutionListenerTests.java @@ -68,7 +68,7 @@ public class JpaFlowExecutionListenerTests extends TestCase { endState.getAttributes().put("commit", Boolean.TRUE); flowSession.setState(endState); - jpaListener.sessionEnded(context, flowSession, null); + jpaListener.sessionEnded(context, flowSession, "success", null); assertEquals("Table should only have two rows", 2, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertSessionNotBound(); assertFalse(flowSession.getScope().contains("hibernate.session")); @@ -99,7 +99,7 @@ public class JpaFlowExecutionListenerTests extends TestCase { endState.getAttributes().put("commit", Boolean.TRUE); flowSession.setState(endState); - jpaListener.sessionEnded(context, flowSession, null); + jpaListener.sessionEnded(context, flowSession, "success", null); assertEquals("Table should only have three rows", 3, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertFalse(flowSession.getScope().contains("hibernate.session")); @@ -123,7 +123,7 @@ public class JpaFlowExecutionListenerTests extends TestCase { EndState endState = new EndState(flowSession.getDefinitionInternal(), "cancel"); endState.getAttributes().put("commit", Boolean.FALSE); flowSession.setState(endState); - jpaListener.sessionEnded(context, flowSession, null); + jpaListener.sessionEnded(context, flowSession, "cancel", null); assertEquals("Table should only have two rows", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertSessionNotBound(); assertFalse(flowSession.getScope().contains("hibernate.session")); @@ -141,7 +141,7 @@ public class JpaFlowExecutionListenerTests extends TestCase { EndState endState = new EndState(flowSession.getDefinitionInternal(), "cancel"); flowSession.setState(endState); - jpaListener.sessionEnded(context, flowSession, null); + jpaListener.sessionEnded(context, flowSession, "cancel", null); assertEquals("Table should only have three rows", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN")); assertFalse(flowSession.getScope().contains("hibernate.session"));