executor refinements; better exception handling, flow handler adapter mvc integration, 1.x usage compatability
This commit is contained in:
@@ -10,8 +10,6 @@ import java.util.Set;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
|
||||
@@ -64,9 +62,7 @@ public class FlowResourceHelper {
|
||||
writer.writeAttribute(key, attributes.get(key), null);
|
||||
}
|
||||
|
||||
FlowDefinitionRequestInfo requestInfo = new FlowDefinitionRequestInfo("resources", new RequestPath(scriptPath),
|
||||
null, null);
|
||||
String src = requestContext.getExternalContext().buildFlowDefinitionUrl(requestInfo);
|
||||
String src = requestContext.getExternalContext().getContextPath() + "/resources" + scriptPath;
|
||||
|
||||
writer.writeAttribute("src", src, null);
|
||||
|
||||
@@ -96,9 +92,7 @@ public class FlowResourceHelper {
|
||||
writer.writeAttribute("type", "text/css", null);
|
||||
writer.writeAttribute("rel", "stylesheet", null);
|
||||
|
||||
FlowDefinitionRequestInfo requestInfo = new FlowDefinitionRequestInfo("resources", new RequestPath(cssPath),
|
||||
null, null);
|
||||
String src = requestContext.getExternalContext().buildFlowDefinitionUrl(requestInfo);
|
||||
String src = requestContext.getExternalContext().getContextPath() + "/resources" + cssPath;
|
||||
|
||||
writer.writeAttribute("href", src, null);
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ public class JsfView implements View {
|
||||
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
FacesContext defaultFacesContext = facesContextFactory.getFacesContext(context.getExternalContext()
|
||||
.getContext(), context.getExternalContext().getRequest(), context.getExternalContext().getResponse(),
|
||||
facesLifecycle);
|
||||
.getNativeContext(), context.getExternalContext().getNativeRequest(), context.getExternalContext()
|
||||
.getNativeResponse(), facesLifecycle);
|
||||
return new FlowFacesContext(context, defaultFacesContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,8 +129,8 @@ public class JsfViewFactory implements ViewFactory {
|
||||
FacesContextFactory facesContextFactory = (FacesContextFactory) FactoryFinder
|
||||
.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
|
||||
FacesContext defaultFacesContext = facesContextFactory.getFacesContext(context.getExternalContext()
|
||||
.getContext(), context.getExternalContext().getRequest(), context.getExternalContext().getResponse(),
|
||||
lifecycle);
|
||||
.getNativeContext(), context.getExternalContext().getNativeRequest(), context.getExternalContext()
|
||||
.getNativeResponse(), lifecycle);
|
||||
return new FlowFacesContext(context, defaultFacesContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package org.springframework.faces.ui.resource;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.shale.test.mock.MockResponseWriter;
|
||||
import org.easymock.EasyMock;
|
||||
import org.springframework.faces.webflow.JSFMockHelper;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
|
||||
@@ -39,13 +36,9 @@ public class FlowResourceHelperTests extends TestCase {
|
||||
public final void testRenderScriptLink() throws IOException {
|
||||
|
||||
String scriptPath = "/dojo/dojo.js";
|
||||
String expectedUrl = "/context/spring/resources/dojo/dojo.js";
|
||||
String expectedUrl = "/context/resources/dojo/dojo.js";
|
||||
|
||||
FlowDefinitionRequestInfo expectedRequest = new FlowDefinitionRequestInfo("resources", new RequestPath(
|
||||
scriptPath), null, null);
|
||||
|
||||
EasyMock.expect(externalContext.buildFlowDefinitionUrl(requestInfoMatches(expectedRequest))).andReturn(
|
||||
expectedUrl);
|
||||
EasyMock.expect(externalContext.getContextPath()).andReturn("/context");
|
||||
|
||||
EasyMock.replay(new Object[] { requestContext, externalContext });
|
||||
|
||||
@@ -63,13 +56,9 @@ public class FlowResourceHelperTests extends TestCase {
|
||||
public final void testRenderStyleLink() throws IOException {
|
||||
|
||||
String scriptPath = "/dijit/themes/dijit.css";
|
||||
String expectedUrl = "/context/spring/resources/dijit/themes/dijit.css";
|
||||
String expectedUrl = "/context/resources/dijit/themes/dijit.css";
|
||||
|
||||
FlowDefinitionRequestInfo expectedRequest = new FlowDefinitionRequestInfo("resources", new RequestPath(
|
||||
scriptPath), null, null);
|
||||
|
||||
EasyMock.expect(externalContext.buildFlowDefinitionUrl(requestInfoMatches(expectedRequest))).andReturn(
|
||||
expectedUrl);
|
||||
EasyMock.expect(externalContext.getContextPath()).andReturn("/context");
|
||||
|
||||
EasyMock.replay(new Object[] { requestContext, externalContext });
|
||||
|
||||
@@ -82,9 +71,4 @@ public class FlowResourceHelperTests extends TestCase {
|
||||
|
||||
assertEquals(expectedOutput, writer.toString());
|
||||
}
|
||||
|
||||
static FlowDefinitionRequestInfo requestInfoMatches(FlowDefinitionRequestInfo info) {
|
||||
EasyMock.reportMatcher(new RequestInfoMatcher(info));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.springframework.faces.ui.resource;
|
||||
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
|
||||
class RequestInfoMatcher implements IArgumentMatcher {
|
||||
|
||||
private FlowDefinitionRequestInfo expected;
|
||||
|
||||
public RequestInfoMatcher(FlowDefinitionRequestInfo expected) {
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("/" + expected.getFlowDefinitionId() + expected.getRequestPath());
|
||||
}
|
||||
|
||||
public boolean matches(Object actual) {
|
||||
FlowDefinitionRequestInfo actualRequest = (FlowDefinitionRequestInfo) actual;
|
||||
return expected.getFlowDefinitionId().equals(actualRequest.getFlowDefinitionId())
|
||||
&& expected.getRequestPath().equals(actualRequest.getRequestPath());
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,9 @@ import org.easymock.EasyMock;
|
||||
import org.jboss.el.ExpressionFactoryImpl;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.support.ParserContextImpl;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalParameterMap;
|
||||
@@ -73,7 +75,10 @@ public class JsfFinalResponseActionTests extends TestCase {
|
||||
RequestContext.class).expect(String.class)), null, lifecycle);
|
||||
finalResponseAction = new JsfFinalResponseAction(factory);
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
ExternalContext ext = new MockExternalContext();
|
||||
MockExternalContext ext = new MockExternalContext();
|
||||
ext.setNativeContext(new MockServletContext());
|
||||
ext.setNativeRequest(new MockHttpServletRequest());
|
||||
ext.setNativeResponse(new MockHttpServletResponse());
|
||||
EasyMock.expect(context.getExternalContext()).andStubReturn(ext);
|
||||
AttributeMap flash = new LocalAttributeMap();
|
||||
EasyMock.expect(context.getFlashScope()).andStubReturn(flash);
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ExternalRedirectAction extends AbstractAction {
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
String resourceUri = (String) this.resourceUri.getValue(context);
|
||||
context.getExternalContext().sendExternalRedirect(resourceUri);
|
||||
context.getExternalContext().requestExternalRedirect(resourceUri);
|
||||
return success();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,60 +6,42 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.core.collection.LocalParameterMap;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
public class FlowDefinitionRedirectAction extends AbstractAction {
|
||||
private Expression flowId;
|
||||
private Expression[] requestElements;
|
||||
private Map requestParameters;
|
||||
private Map input;
|
||||
|
||||
public FlowDefinitionRedirectAction(Expression flowId, Expression[] requestElements, Map requestParameters) {
|
||||
public FlowDefinitionRedirectAction(Expression flowId, Map input) {
|
||||
Assert.notNull(flowId, "The flow id to redirect to is required");
|
||||
this.flowId = flowId;
|
||||
this.requestElements = requestElements;
|
||||
this.requestParameters = requestParameters;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
String flowId = (String) this.flowId.getValue(context);
|
||||
RequestPath requestPath = evaluateRequestPath(context);
|
||||
ParameterMap requestParameters = evaluateRequestParameters(context);
|
||||
context.getExternalContext().sendFlowDefinitionRedirect(
|
||||
new FlowDefinitionRequestInfo(flowId, requestPath, requestParameters, null));
|
||||
AttributeMap input = evaluateInput(context);
|
||||
context.getExternalContext().requestFlowDefinitionRedirect(flowId, input);
|
||||
return success();
|
||||
}
|
||||
|
||||
private RequestPath evaluateRequestPath(RequestContext context) {
|
||||
if (this.requestElements == null || this.requestElements.length == 0) {
|
||||
return null;
|
||||
}
|
||||
String[] requestElements = new String[this.requestElements.length];
|
||||
for (int i = 0; i < this.requestElements.length; i++) {
|
||||
Expression element = this.requestElements[i];
|
||||
requestElements[i] = (String) element.getValue(context);
|
||||
}
|
||||
return RequestPath.valueOf(requestElements);
|
||||
}
|
||||
|
||||
private ParameterMap evaluateRequestParameters(RequestContext context) {
|
||||
if (this.requestParameters == null) {
|
||||
private AttributeMap evaluateInput(RequestContext context) {
|
||||
if (this.input == null) {
|
||||
return null;
|
||||
} else {
|
||||
Map requestParameters = new HashMap();
|
||||
for (Iterator it = this.requestParameters.entrySet().iterator(); it.hasNext();) {
|
||||
Map input = new HashMap();
|
||||
for (Iterator it = this.input.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Expression name = (Expression) entry.getKey();
|
||||
Expression value = (Expression) entry.getValue();
|
||||
String paramName = (String) name.getValue(context);
|
||||
String paramValue = (String) value.getValue(context);
|
||||
requestParameters.put(paramName, paramValue);
|
||||
Object paramValue = value.getValue(context);
|
||||
input.put(paramName, paramValue);
|
||||
}
|
||||
return new LocalParameterMap(requestParameters);
|
||||
return new LocalAttributeMap(input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
|
||||
/**
|
||||
* An abstract representation request to be sent into the Spring Web Flow system at a later date. Used to request
|
||||
* operations such as flow definition redirects, flow execution redirects, as well as to generate flow definition and
|
||||
* flow execution URLs.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public abstract class AbstractFlowRequestInfo {
|
||||
|
||||
/**
|
||||
* The flow definition the request should be sent to.
|
||||
*/
|
||||
private String flowDefinitionId;
|
||||
|
||||
/**
|
||||
* Hierarchical data to be sent along in the request.
|
||||
*/
|
||||
private RequestPath requestPath;
|
||||
|
||||
/**
|
||||
* Query parameters to be sent along in the request.
|
||||
*/
|
||||
private ParameterMap requestParameters;
|
||||
|
||||
/**
|
||||
* An anchor fragment to be sent in the request, for interpretation by the agent that initiates the request.
|
||||
*/
|
||||
private String fragment;
|
||||
|
||||
protected AbstractFlowRequestInfo(String flowDefinitionId, RequestPath requestPath, ParameterMap requestParameters,
|
||||
String fragment) {
|
||||
this.flowDefinitionId = flowDefinitionId;
|
||||
this.requestPath = requestPath;
|
||||
this.requestParameters = requestParameters;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flow definition this request should be sent to.
|
||||
* @return the flow definition identifier
|
||||
*/
|
||||
public String getFlowDefinitionId() {
|
||||
return flowDefinitionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hierarchical data to be sent along in the request.
|
||||
* @return the request path
|
||||
*/
|
||||
public RequestPath getRequestPath() {
|
||||
return requestPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns query parameters to send along in the request.
|
||||
* @return the request parameters
|
||||
*/
|
||||
public ParameterMap getRequestParameters() {
|
||||
return requestParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fragment to be sent along in the request. A fragment is a free-form string value that may be
|
||||
* interpreted by the client agent that initiates the request.
|
||||
* @return the fragment
|
||||
*/
|
||||
public String getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
@@ -40,31 +40,10 @@ import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
public interface ExternalContext {
|
||||
|
||||
/**
|
||||
* Returns the unique id of the flow definition invoked by this caller. For a "launch" request, this identifier is
|
||||
* used directly to create a new flow execution. For a "resume" request, this identifier provides additional flow
|
||||
* execution context.
|
||||
* @return the flow definition identifier, never null
|
||||
* @see #getFlowExecutionKey()
|
||||
* Returns the logical path to the application hosting this external context.
|
||||
* @return the context path
|
||||
*/
|
||||
public String getFlowId();
|
||||
|
||||
/**
|
||||
* Returns the unique key identifying the flow execution this client wishes to resume.
|
||||
* @return the flow execution key, may be null if this is not a resume request
|
||||
*/
|
||||
public String getFlowExecutionKey();
|
||||
|
||||
/**
|
||||
* Returns the type of this client request. For example, this request may be a "GET" request or a "POST" request.
|
||||
* @return the request method
|
||||
*/
|
||||
public String getRequestMethod();
|
||||
|
||||
/**
|
||||
* Returns the path of this request as a ordered list of fields.
|
||||
* @return the elements of the request path
|
||||
*/
|
||||
public RequestPath getRequestPath();
|
||||
public String getContextPath();
|
||||
|
||||
/**
|
||||
* Provides access to the parameters associated with the user request that led to SWF being called. This map is
|
||||
@@ -105,113 +84,82 @@ public interface ExternalContext {
|
||||
*/
|
||||
public SharedAttributeMap getApplicationMap();
|
||||
|
||||
/**
|
||||
* Returns true if the current request is an Ajax request.
|
||||
* @return true if the current request is an Ajax request
|
||||
*/
|
||||
public boolean isAjaxRequest();
|
||||
|
||||
/**
|
||||
* Get a context-relative flow execution URL for the execution with the provided key. Typically used by response
|
||||
* writers that write out references to the flow execution to support postback on a subsequent request.
|
||||
* @param flowId the flow definition id
|
||||
* @param flowExecutionKey the flow execution key
|
||||
* @return the flow execution URL
|
||||
*/
|
||||
public String getFlowExecutionUri(String flowId, String flowExecutionKey);
|
||||
|
||||
/**
|
||||
* Provides access to the context object for the current environment.
|
||||
* @return the environment specific context object
|
||||
*/
|
||||
public Object getContext();
|
||||
public Object getNativeContext();
|
||||
|
||||
/**
|
||||
* Provides access to the request object for the current environment.
|
||||
* @return the environment specific request object.
|
||||
*/
|
||||
public Object getRequest();
|
||||
public Object getNativeRequest();
|
||||
|
||||
/**
|
||||
* Provides access to the response object for the current environment.
|
||||
* @return the environment specific response object.
|
||||
*/
|
||||
public Object getResponse();
|
||||
public Object getNativeResponse();
|
||||
|
||||
/**
|
||||
* Get a writer for writing out a response.
|
||||
* @return the writer
|
||||
*/
|
||||
public PrintWriter getResponseWriter();
|
||||
public Writer getResponseWriter();
|
||||
|
||||
/**
|
||||
* Builds a context-relative flow definition URL, suitable for rendering links that a launch new execution of a flow
|
||||
* definition when accessed.
|
||||
* @param requestInfo data needed to build the flow definition path
|
||||
* @return the generated flow definition URL
|
||||
* Sets an entry in the response header.
|
||||
* @param name the entry name
|
||||
* @param value the entry value
|
||||
*/
|
||||
public String buildFlowDefinitionUrl(FlowDefinitionRequestInfo requestInfo);
|
||||
public void setResponseHeader(String name, String value);
|
||||
|
||||
/**
|
||||
* Builds a flow execution URL, suitable for rendering links that resume a paused flow execution when accessed.
|
||||
* @param requestInfo data needed to build the flow execution URL
|
||||
* @param contextRelative whether the URL returned should be relative to this external context or absolute.
|
||||
* @return the generated flow execution URL
|
||||
*/
|
||||
public String buildFlowExecutionUrl(FlowExecutionRequestInfo requestInfo, boolean contextRelative);
|
||||
|
||||
/**
|
||||
* Encode the provided string using the encoding scheme of this external context.
|
||||
* @param string the string
|
||||
* @return the encoded string
|
||||
*/
|
||||
public String encode(String string);
|
||||
|
||||
/**
|
||||
* Request that a flow execution redirect be sent as the response. A flow execution redirect tells the caller to
|
||||
* resume a flow execution in a new request. Sets response committed to true.
|
||||
* @param requestInfo data needed to issue the flow execution redirect
|
||||
* Request that a flow execution redirect be performed by the calling environment. Typically called from within a
|
||||
* flow execution to request a refresh operation, usually to support "refresh after event processing" behavior.
|
||||
* Calling this method commits the response.
|
||||
* @see #isResponseCommitted()
|
||||
*/
|
||||
public void sendFlowExecutionRedirect(FlowExecutionRequestInfo requestInfo);
|
||||
public void requestFlowExecutionRedirect();
|
||||
|
||||
/**
|
||||
* Request that a flow definition redirect be sent as the response. A flow definition redirect tells the caller to
|
||||
* start a new execution of the flow definition with the input provided.
|
||||
* @param requestInfo data needed to issue the flow definition redirect
|
||||
*/
|
||||
public void sendFlowDefinitionRedirect(FlowDefinitionRequestInfo requestInfo);
|
||||
|
||||
/**
|
||||
* Request that a external redirect be sent as the response. An external redirect tells the caller to access the
|
||||
* resource at the given resource URL. Sets response committed to true. Note: no special encoding is performed on
|
||||
* the string argument. Callers must perform their own encoding when necessary.
|
||||
* @param resourceUrl the resource URL string
|
||||
* Request that a flow definition redirect be performed by the calling environment. Typically called from within a
|
||||
* flow execution end state to request starting a new, independent execution of a flow in a chain-like manner.
|
||||
* Calling this method commits the response.
|
||||
* @see #isResponseCommitted()
|
||||
* @param flowId the id of the flow definition to redirect to
|
||||
* @param input input to pass the flow; this input is generally encoded the url to launch the flow
|
||||
*/
|
||||
public void sendExternalRedirect(String resourceUrl);
|
||||
public void requestFlowDefinitionRedirect(String flowId, AttributeMap input);
|
||||
|
||||
/**
|
||||
* Report that flow execution request processing ended with a "paused" result, indicating the flow execution paused
|
||||
* and will be waiting to resume on a subsequent request.
|
||||
* @param flowExecutionKey the flow execution key
|
||||
* Request a redirect to an arbitrary resource URI. May not be supported in some environments. Calling this method
|
||||
* commits the response.
|
||||
* @see #isResponseCommitted()
|
||||
* @param uri the URI to redirect to
|
||||
*/
|
||||
public void setPausedResult(String flowExecutionKey);
|
||||
public void requestExternalRedirect(String uri);
|
||||
|
||||
/**
|
||||
* Report that flow execution request processing ended with a "ended" result, indicating the flow execution
|
||||
* terminated.
|
||||
* @param flowExecutionKey the flow execution key, now invalid or null if never assigned
|
||||
*/
|
||||
public void setEndedResult(String flowExecutionKey);
|
||||
|
||||
/**
|
||||
* Report that flow execution request processing ended with a flow exception.
|
||||
* @param e the flow exception
|
||||
*/
|
||||
public void setExceptionResult(FlowException e);
|
||||
|
||||
/**
|
||||
* Returns true if the current request has already provisioned the response that will be sent back to the calling
|
||||
* system.
|
||||
* @return true if the response has been committed, false otherwise
|
||||
* @see #sendFlowExecutionRedirect(FlowExecutionRequestInfo)
|
||||
* @see #sendFlowDefinitionRedirect(FlowDefinitionRequestInfo)
|
||||
* @see #sendExternalRedirect(String)
|
||||
* Has the response been committed?
|
||||
* @return true if yes, false otherwise
|
||||
*/
|
||||
public boolean isResponseCommitted();
|
||||
|
||||
/**
|
||||
* Returns true if the current request is an Ajax request, determined by the value of the http accept header.
|
||||
* @return true if the current request is an Ajax request
|
||||
*/
|
||||
public boolean isAjaxRequest();
|
||||
|
||||
public void setResponseHeader(String name, String value);
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
|
||||
/**
|
||||
* A request to launch a new execution of a flow definition.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class FlowDefinitionRequestInfo extends AbstractFlowRequestInfo {
|
||||
|
||||
/**
|
||||
* Creates a new flow definition request.
|
||||
* @param flowDefinitionId the flow definition id (required)
|
||||
* @param requestPath the request path (optional)
|
||||
* @param requestParameters the request parameters (optional)
|
||||
* @param fragment the fragment (optional)
|
||||
*/
|
||||
public FlowDefinitionRequestInfo(String flowDefinitionId, RequestPath requestPath, ParameterMap requestParameters,
|
||||
String fragment) {
|
||||
super(flowDefinitionId, requestPath, requestParameters, fragment);
|
||||
Assert.hasText(flowDefinitionId, "The id of the flow definition to redirect to is required");
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
|
||||
/**
|
||||
* A request to resume an existing flow execution that is currently paused.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class FlowExecutionRequestInfo extends AbstractFlowRequestInfo {
|
||||
|
||||
/**
|
||||
* The key of the flow execution to resume.
|
||||
*/
|
||||
private String flowExecutionKey;
|
||||
|
||||
/**
|
||||
* Creates a new request to resume a flow execution.
|
||||
* @param flowDefinitionId the definition identifier of the execution (required)
|
||||
* @param flowExecutionKey the key of the flow execution (required)
|
||||
*/
|
||||
public FlowExecutionRequestInfo(String flowDefinitionId, String flowExecutionKey) {
|
||||
this(flowDefinitionId, flowExecutionKey, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new request to resume a flow execution.
|
||||
* @param flowDefinitionId the definition identifier of the execution (required)
|
||||
* @param flowExecutionKey the key of the flow execution (required)
|
||||
* @param requestPath the request path (optional)
|
||||
* @param requestParameters the request parameters (optional)
|
||||
* @param fragment the fragment (optional)
|
||||
*/
|
||||
public FlowExecutionRequestInfo(String flowDefinitionId, String flowExecutionKey, RequestPath requestPath,
|
||||
ParameterMap requestParameters, String fragment) {
|
||||
super(flowDefinitionId, requestPath, requestParameters, fragment);
|
||||
Assert.hasText(flowDefinitionId,
|
||||
"The definition identifier of the flow execution to redirect to cannot be null");
|
||||
Assert.hasText(flowExecutionKey, "The flow execution key to redirect to cannot be null");
|
||||
this.flowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique key of the flow execution to resume.
|
||||
* @return the flow execution key
|
||||
*/
|
||||
public String getFlowExecutionKey() {
|
||||
return flowExecutionKey;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A representation of a flow request path. In a flow definition request URI,the request path is the portion after the
|
||||
* flow definition identifier. For example, in a URI of http://host/booking-flow/1 the request path is "/1".
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class RequestPath {
|
||||
|
||||
private String[] elements;
|
||||
|
||||
public RequestPath(String path) {
|
||||
Assert.notNull(path, "The path string cannot be null");
|
||||
Assert.isTrue(path.startsWith("/"), "The path must start with a '/'");
|
||||
this.elements = path.substring(1).split("/");
|
||||
}
|
||||
|
||||
private RequestPath(String[] elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public RequestPath pop(int elementCount) {
|
||||
if (elementCount < elements.length) {
|
||||
String[] newElements = new String[elements.length - elementCount];
|
||||
System.arraycopy(elements, elementCount, newElements, 0, newElements.length);
|
||||
return new RequestPath(newElements);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public RequestPath pop() {
|
||||
return pop(1);
|
||||
}
|
||||
|
||||
public int getElementCount() {
|
||||
return elements.length;
|
||||
}
|
||||
|
||||
public String getFirstElement() {
|
||||
return getElement(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the elements of the request path.
|
||||
* @return parsed request path elements
|
||||
*/
|
||||
public String[] getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path element at the specified index.
|
||||
* @param index the element index
|
||||
* @return the element
|
||||
* @throws IndexOutOfBoundsException if the index is out of bounds
|
||||
*/
|
||||
public String getElement(int index) throws IndexOutOfBoundsException {
|
||||
return elements[index];
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof RequestPath)) {
|
||||
return false;
|
||||
}
|
||||
RequestPath other = (RequestPath) o;
|
||||
return Arrays.equals(this.elements, other.elements);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(elements);
|
||||
}
|
||||
|
||||
public static RequestPath valueOf(String[] elements) {
|
||||
return new RequestPath(elements);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("/");
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
buffer.append(elements[i]);
|
||||
if (i < (elements.length - 1)) {
|
||||
buffer.append("/");
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.binding.collection.CompositeIterator;
|
||||
import org.springframework.binding.collection.StringKeyedMapAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
|
||||
@@ -41,6 +42,7 @@ public class HttpServletRequestParameterMap extends StringKeyedMapAdapter {
|
||||
* Create a new map wrapping the parameters of given request.
|
||||
*/
|
||||
public HttpServletRequestParameterMap(HttpServletRequest request) {
|
||||
Assert.notNull(request, "The HTTP servlet request is required");
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,34 +16,21 @@
|
||||
package org.springframework.webflow.context.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.io.Writer;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.webflow.context.AbstractFlowRequestInfo;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.ExternalContextHolder;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
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.LocalParameterMap;
|
||||
import org.springframework.webflow.core.collection.LocalSharedAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
import org.springframework.webflow.executor.FlowExecutor;
|
||||
|
||||
/**
|
||||
* Provides contextual information about an HTTP Servlet environment that has interacted with Spring Web Flow.
|
||||
@@ -54,18 +41,12 @@ import org.springframework.webflow.executor.FlowExecutor;
|
||||
*/
|
||||
public class ServletExternalContext implements ExternalContext {
|
||||
|
||||
/** The default encoding scheme: UTF-8 */
|
||||
private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
|
||||
|
||||
/** The accept header value that signifies an Ajax request */
|
||||
private static final String AJAX_ACCEPT_CONTENT_TYPE = "text/html;type=ajax";
|
||||
|
||||
/** Alternate request paramater to indicate an ajax request for cases when control of the header is not available */
|
||||
/** Alternate request parameter to indicate an Ajax request for cases when control of the header is not available */
|
||||
private static final String AJAX_SOURCE_PARAM = "ajaxSource";
|
||||
|
||||
/** The response header to be set on an Ajax redirect */
|
||||
private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL";
|
||||
|
||||
/**
|
||||
* The context.
|
||||
*/
|
||||
@@ -101,83 +82,59 @@ public class ServletExternalContext implements ExternalContext {
|
||||
*/
|
||||
private SharedAttributeMap applicationMap;
|
||||
|
||||
private FlowRequestInfo flowRequestInfo;
|
||||
/**
|
||||
* A flag indicating if a flow execution redirect has been requested.
|
||||
*/
|
||||
private boolean flowExecutionRedirectRequested;
|
||||
|
||||
private String encodingScheme = DEFAULT_ENCODING_SCHEME;
|
||||
/**
|
||||
* A string specifying the id of the flow to redirect to after request processing. If null, no flow definition
|
||||
* redirect has been requested.
|
||||
*/
|
||||
private String flowDefinitionRedirectFlowId;
|
||||
|
||||
private FlowExecutionRedirector flowExecutionRedirector;
|
||||
/**
|
||||
* Input to pass the flow definition upon redirecting. May be null. Never set unless
|
||||
* {@link #flowDefinitionRedirectFlowId} has been set.
|
||||
*/
|
||||
private AttributeMap flowDefinitionRedirectFlowInput;
|
||||
|
||||
private FlowDefinitionRedirector flowDefinitionRedirector;
|
||||
/**
|
||||
* A string specifying an arbitrary
|
||||
*/
|
||||
private String externalRedirectUrl;
|
||||
|
||||
private String resourceUri;
|
||||
|
||||
private short result;
|
||||
|
||||
private String processedFlowExecutionKey;
|
||||
|
||||
private FlowException exception;
|
||||
/**
|
||||
* The strategy for generating flow execution urls.
|
||||
*/
|
||||
private FlowUrlHandler flowUrlHandler;
|
||||
|
||||
/**
|
||||
* Create a new external context wrapping given servlet HTTP request and response and given servlet context.
|
||||
* @param context the servlet context
|
||||
* @param request the servlet request
|
||||
* @param response the servlet response
|
||||
* @param request the http servlet request
|
||||
* @param response the http servlet response
|
||||
*/
|
||||
public ServletExternalContext(ServletContext context, ServletRequest request, ServletResponse response) {
|
||||
this.context = context;
|
||||
try {
|
||||
this.request = (HttpServletRequest) request;
|
||||
this.response = (HttpServletResponse) response;
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalArgumentException("Request and response objects must be HTTP objects", e);
|
||||
}
|
||||
this.requestParameterMap = new LocalParameterMap(new HttpServletRequestParameterMap(this.request));
|
||||
this.requestMap = new LocalAttributeMap(new HttpServletRequestMap(this.request));
|
||||
this.sessionMap = new LocalSharedAttributeMap(new HttpSessionMap(this.request));
|
||||
this.applicationMap = new LocalSharedAttributeMap(new HttpServletContextMap(context));
|
||||
this.flowRequestInfo = parseFlowRequestInfo(this.request);
|
||||
public ServletExternalContext(ServletContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
init(context, request, response, new DefaultFlowUrlHandler());
|
||||
}
|
||||
|
||||
public static class FlowRequestInfo {
|
||||
private String flowId;
|
||||
|
||||
private String flowExecutionKey;
|
||||
|
||||
private RequestPath requestPath;
|
||||
|
||||
public FlowRequestInfo(String flowId, String flowExecutionKey, RequestPath requestPath) {
|
||||
this.flowId = flowId;
|
||||
this.flowExecutionKey = flowExecutionKey;
|
||||
this.requestPath = requestPath;
|
||||
}
|
||||
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public String getFlowExecutionKey() {
|
||||
return flowExecutionKey;
|
||||
}
|
||||
|
||||
public RequestPath getRequestPath() {
|
||||
return requestPath;
|
||||
}
|
||||
/**
|
||||
* Create a new external context wrapping given servlet HTTP request and response and given servlet context.
|
||||
* @param context the servlet context
|
||||
* @param request the http servlet request
|
||||
* @param response the http servlet response
|
||||
* @param flowUrlHandler the flow url handler
|
||||
*/
|
||||
public ServletExternalContext(ServletContext context, HttpServletRequest request, HttpServletResponse response,
|
||||
FlowUrlHandler flowUrlHandler) {
|
||||
init(context, request, response, flowUrlHandler);
|
||||
}
|
||||
|
||||
public String getFlowId() {
|
||||
return flowRequestInfo.getFlowId();
|
||||
}
|
||||
// implementing external context
|
||||
|
||||
public String getFlowExecutionKey() {
|
||||
return flowRequestInfo.getFlowExecutionKey();
|
||||
}
|
||||
|
||||
public RequestPath getRequestPath() {
|
||||
return flowRequestInfo.getRequestPath();
|
||||
}
|
||||
|
||||
public String getRequestMethod() {
|
||||
return request.getMethod();
|
||||
public String getContextPath() {
|
||||
return request.getContextPath();
|
||||
}
|
||||
|
||||
public ParameterMap getRequestParameterMap() {
|
||||
@@ -200,283 +157,18 @@ public class ServletExternalContext implements ExternalContext {
|
||||
return applicationMap;
|
||||
}
|
||||
|
||||
public Object getContext() {
|
||||
public Object getNativeContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public Object getRequest() {
|
||||
public Object getNativeRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public Object getResponse() {
|
||||
public Object getNativeResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public boolean isResponseCommitted() {
|
||||
return flowExecutionRedirector != null || flowDefinitionRedirector != null || resourceUri != null;
|
||||
}
|
||||
|
||||
public PrintWriter getResponseWriter() {
|
||||
try {
|
||||
return response.getWriter();
|
||||
} catch (IOException e) {
|
||||
// TODO - handle how?
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String encode(String string) {
|
||||
try {
|
||||
return URLEncoder.encode(string, encodingScheme);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Unsupported encoding errors should never happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String buildFlowDefinitionUrl(FlowDefinitionRequestInfo requestInfo) {
|
||||
return getFlowExecutorPath() + "/" + requestInfo.getFlowDefinitionId() + requestPath(requestInfo)
|
||||
+ requestParameters(requestInfo) + fragment(requestInfo);
|
||||
}
|
||||
|
||||
public String buildFlowExecutionUrl(FlowExecutionRequestInfo requestInfo, boolean contextRelative) {
|
||||
String contextRelativeUrl = getFlowExecutionsPath() + requestInfo.getFlowDefinitionId() + "/"
|
||||
+ requestInfo.getFlowExecutionKey() + requestPath(requestInfo) + requestParameters(requestInfo)
|
||||
+ fragment(requestInfo);
|
||||
if (contextRelative) {
|
||||
return contextRelativeUrl;
|
||||
} else {
|
||||
return request.getScheme() + request.getServerName() + contextRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFlowExecutionRedirect(FlowExecutionRequestInfo request) {
|
||||
flowExecutionRedirector = new FlowExecutionRedirector(request);
|
||||
}
|
||||
|
||||
public void sendFlowDefinitionRedirect(FlowDefinitionRequestInfo request) {
|
||||
flowDefinitionRedirector = new FlowDefinitionRedirector(request);
|
||||
}
|
||||
|
||||
public void sendExternalRedirect(String resourceUri) {
|
||||
this.resourceUri = resourceUri;
|
||||
}
|
||||
|
||||
// execution processing result setters
|
||||
|
||||
public void setPausedResult(String flowExecutionKey) {
|
||||
result = 0;
|
||||
processedFlowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
public void setEndedResult(String flowExecutionKey) {
|
||||
result = 1;
|
||||
processedFlowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
public void setExceptionResult(FlowException e) {
|
||||
result = 2;
|
||||
exception = e;
|
||||
processedFlowExecutionKey = flowRequestInfo.getFlowExecutionKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the flow request lifecycle, including provision of the final response.
|
||||
* @param flowExecutor the flow executor for calling into the Spring Web Flow system
|
||||
* @throws IOException an IOException occurred issuing the response
|
||||
*/
|
||||
public void executeFlowRequest(FlowExecutor flowExecutor) throws IOException {
|
||||
ExternalContextHolder.setExternalContext(this);
|
||||
try {
|
||||
flowExecutor.executeFlowRequest(this);
|
||||
if (isPausedResult()) {
|
||||
if (flowExecutionRedirector != null) {
|
||||
flowExecutionRedirector.issueRedirect();
|
||||
} else if (flowDefinitionRedirector != null) {
|
||||
flowDefinitionRedirector.issueRedirect();
|
||||
} else if (resourceUri != null) {
|
||||
sendRedirect(resourceUri);
|
||||
} else {
|
||||
// commit response?
|
||||
}
|
||||
} else if (isEndResult()) {
|
||||
if (flowExecutionRedirector != null) {
|
||||
if (flowExecutionRedirector.redirectsTo(processedFlowExecutionKey)) {
|
||||
throw new IllegalStateException(
|
||||
"You cannot send a flow execution redirect when this execution has ended - programmer error");
|
||||
} else {
|
||||
flowExecutionRedirector.issueRedirect();
|
||||
}
|
||||
} else if (flowDefinitionRedirector != null) {
|
||||
flowDefinitionRedirector.issueRedirect();
|
||||
} else if (resourceUri != null) {
|
||||
sendRedirect(resourceUri);
|
||||
} else {
|
||||
// commit response?
|
||||
}
|
||||
} else if (isExceptionResult()) {
|
||||
// response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, exception.getMessage());
|
||||
throw exception;
|
||||
}
|
||||
} finally {
|
||||
ExternalContextHolder.setExternalContext(null);
|
||||
}
|
||||
}
|
||||
|
||||
// hooks subclasses may override
|
||||
|
||||
protected FlowRequestInfo parseFlowRequestInfo(HttpServletRequest request) {
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (pathInfo == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"The requestPathInfo is null: unable to extract flow definition id or flow execution key");
|
||||
}
|
||||
String flowId;
|
||||
String flowExecutionKey;
|
||||
RequestPath path = new RequestPath(pathInfo);
|
||||
if (path.getElement(0).equals("executions")) {
|
||||
flowId = path.getElement(1);
|
||||
flowExecutionKey = path.getElement(2);
|
||||
if (path.getElementCount() > 3) {
|
||||
path = path.pop(3);
|
||||
} else {
|
||||
path = null;
|
||||
}
|
||||
} else {
|
||||
flowId = path.getElement(0);
|
||||
flowExecutionKey = null;
|
||||
if (path.getElementCount() > 1) {
|
||||
path = path.pop(1);
|
||||
} else {
|
||||
path = null;
|
||||
}
|
||||
}
|
||||
return new FlowRequestInfo(flowId, flowExecutionKey, path);
|
||||
}
|
||||
|
||||
// private helpers
|
||||
|
||||
private String getFlowExecutorPath() {
|
||||
return request.getContextPath() + request.getServletPath();
|
||||
}
|
||||
|
||||
private String getFlowExecutionsPath() {
|
||||
return getFlowExecutorPath() + "/executions/";
|
||||
}
|
||||
|
||||
private boolean isPausedResult() {
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
private boolean isEndResult() {
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
private boolean isExceptionResult() {
|
||||
return result == 2;
|
||||
}
|
||||
|
||||
private String requestPath(AbstractFlowRequestInfo requestInfo) {
|
||||
if (requestInfo.getRequestPath() == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String[] requestElements = requestInfo.getRequestPath().getElements();
|
||||
for (int i = 0; i < requestElements.length; i++) {
|
||||
buffer.append('/').append(encode(requestElements[i], encodingScheme));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private String requestParameters(AbstractFlowRequestInfo requestInfo) {
|
||||
if (requestInfo.getRequestParameters() == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuffer queryString = new StringBuffer();
|
||||
queryString.append('?');
|
||||
ParameterMap requestParameters = requestInfo.getRequestParameters();
|
||||
Iterator it = requestParameters.asMap().entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String parameterName = encode((String) entry.getKey(), encodingScheme);
|
||||
String parameterValue = encode((String) entry.getValue(), encodingScheme);
|
||||
queryString.append(parameterName).append('=').append(parameterValue);
|
||||
if (it.hasNext()) {
|
||||
queryString.append('&');
|
||||
}
|
||||
}
|
||||
return queryString.toString();
|
||||
}
|
||||
|
||||
private String fragment(AbstractFlowRequestInfo requestInfo) {
|
||||
if (requestInfo.getFragment() == null || requestInfo.getFragment().length() == 0) {
|
||||
return "";
|
||||
}
|
||||
return "#" + requestInfo.getFragment();
|
||||
}
|
||||
|
||||
private String encode(String value, String encodingScheme) {
|
||||
try {
|
||||
return URLEncoder.encode(value, encodingScheme);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendRedirect(String targetUrl) throws IOException {
|
||||
if (isAjaxRequest()) {
|
||||
setResponseHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl));
|
||||
} else {
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("requestParameterMap", getRequestParameterMap()).toString();
|
||||
}
|
||||
|
||||
private abstract class FlowRedirector {
|
||||
private AbstractFlowRequestInfo requestInfo;
|
||||
|
||||
public FlowRedirector(AbstractFlowRequestInfo requestInfo) {
|
||||
this.requestInfo = requestInfo;
|
||||
}
|
||||
|
||||
protected AbstractFlowRequestInfo getRequestInfo() {
|
||||
return requestInfo;
|
||||
}
|
||||
|
||||
public abstract void issueRedirect() throws IOException;
|
||||
}
|
||||
|
||||
private class FlowExecutionRedirector extends FlowRedirector {
|
||||
public FlowExecutionRedirector(FlowExecutionRequestInfo requestInfo) {
|
||||
super(requestInfo);
|
||||
}
|
||||
|
||||
public boolean redirectsTo(String flowExecutionKey) {
|
||||
FlowExecutionRequestInfo requestInfo = (FlowExecutionRequestInfo) getRequestInfo();
|
||||
return requestInfo.getFlowExecutionKey().equals(flowExecutionKey);
|
||||
}
|
||||
|
||||
public void issueRedirect() throws IOException {
|
||||
FlowExecutionRequestInfo requestInfo = (FlowExecutionRequestInfo) getRequestInfo();
|
||||
String targetUrl = buildFlowExecutionUrl(requestInfo, true);
|
||||
sendRedirect(targetUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private class FlowDefinitionRedirector extends FlowRedirector {
|
||||
public FlowDefinitionRedirector(FlowDefinitionRequestInfo redirect) {
|
||||
super(redirect);
|
||||
}
|
||||
|
||||
public void issueRedirect() throws IOException {
|
||||
FlowDefinitionRequestInfo requestInfo = (FlowDefinitionRequestInfo) getRequestInfo();
|
||||
String targetUrl = buildFlowDefinitionUrl(requestInfo);
|
||||
sendRedirect(targetUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAjaxRequest() {
|
||||
String acceptHeader = request.getHeader("Accept");
|
||||
String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM);
|
||||
@@ -487,7 +179,75 @@ public class ServletExternalContext implements ExternalContext {
|
||||
}
|
||||
}
|
||||
|
||||
public String getFlowExecutionUri(String flowId, String flowExecutionKey) {
|
||||
return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, request);
|
||||
}
|
||||
|
||||
public Writer getResponseWriter() {
|
||||
try {
|
||||
return response.getWriter();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unable to obtain response writer", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setResponseHeader(String name, String value) {
|
||||
response.setHeader(name, value);
|
||||
}
|
||||
|
||||
public boolean isResponseCommitted() {
|
||||
return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested();
|
||||
}
|
||||
|
||||
public void requestFlowExecutionRedirect() {
|
||||
flowExecutionRedirectRequested = true;
|
||||
}
|
||||
|
||||
public void requestExternalRedirect(String uri) {
|
||||
externalRedirectUrl = uri;
|
||||
}
|
||||
|
||||
public void requestFlowDefinitionRedirect(String flowId, AttributeMap input) {
|
||||
flowDefinitionRedirectFlowId = flowId;
|
||||
flowDefinitionRedirectFlowInput = input;
|
||||
}
|
||||
|
||||
// implementation specific methods
|
||||
|
||||
public boolean flowExecutionRedirectRequested() {
|
||||
return flowExecutionRedirectRequested;
|
||||
}
|
||||
|
||||
public boolean flowDefinitionRedirectRequested() {
|
||||
return flowDefinitionRedirectFlowId != null;
|
||||
}
|
||||
|
||||
public String getFlowRedirectFlowId() {
|
||||
return flowDefinitionRedirectFlowId;
|
||||
}
|
||||
|
||||
public AttributeMap getFlowRedirectFlowInput() {
|
||||
return flowDefinitionRedirectFlowInput;
|
||||
}
|
||||
|
||||
public boolean externalRedirectRequested() {
|
||||
return externalRedirectUrl != null;
|
||||
}
|
||||
|
||||
public String getExternalRedirectUrl() {
|
||||
return externalRedirectUrl;
|
||||
}
|
||||
|
||||
// private helpers
|
||||
|
||||
private void init(ServletContext context, HttpServletRequest request, HttpServletResponse response,
|
||||
FlowUrlHandler flowUrlHandler) {
|
||||
this.context = context;
|
||||
this.requestParameterMap = new LocalParameterMap(new HttpServletRequestParameterMap(request));
|
||||
this.requestMap = new LocalAttributeMap(new HttpServletRequestMap(request));
|
||||
this.sessionMap = new LocalSharedAttributeMap(new HttpSessionMap(request));
|
||||
this.applicationMap = new LocalSharedAttributeMap(new HttpServletContextMap(context));
|
||||
this.flowUrlHandler = flowUrlHandler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -140,6 +140,8 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
*/
|
||||
private Serializable messagesMemento;
|
||||
|
||||
private transient Event outcome;
|
||||
|
||||
/**
|
||||
* Default constructor required for externalizable serialization. Should NOT be called programmatically.
|
||||
*/
|
||||
@@ -186,6 +188,14 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
return !flowSessions.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasEnded() {
|
||||
return hasStarted() && !isActive();
|
||||
}
|
||||
|
||||
public Event getOutcome() {
|
||||
return outcome;
|
||||
}
|
||||
|
||||
public FlowSession getActiveSession() {
|
||||
if (!isActive()) {
|
||||
if (started) {
|
||||
@@ -211,7 +221,8 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
|
||||
// methods implementing FlowExecution
|
||||
|
||||
public void start(ExternalContext externalContext) throws FlowExecutionException, IllegalStateException {
|
||||
public void start(MutableAttributeMap input, ExternalContext externalContext) throws FlowExecutionException,
|
||||
IllegalStateException {
|
||||
Assert.state(!started, "This flow has already been started; you cannot call 'start()' more than once");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Starting execution in " + externalContext);
|
||||
@@ -221,13 +232,13 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
listeners.fireRequestSubmitted(context);
|
||||
try {
|
||||
start(flow, flow.createExecutionInputMap(externalContext), context);
|
||||
start(flow, input, context);
|
||||
} catch (FlowExecutionException e) {
|
||||
handleException(e, context);
|
||||
} catch (Exception e) {
|
||||
handleException(wrap(e), context);
|
||||
} finally {
|
||||
if (isActive()) {
|
||||
if (!hasEnded()) {
|
||||
saveMessages(context);
|
||||
try {
|
||||
listeners.firePaused(context);
|
||||
@@ -266,7 +277,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
} catch (Exception e) {
|
||||
handleException(wrap(e), context);
|
||||
} finally {
|
||||
if (isActive()) {
|
||||
if (!hasEnded()) {
|
||||
saveMessages(context);
|
||||
try {
|
||||
listeners.firePaused(context);
|
||||
@@ -348,6 +359,9 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
|
||||
session.getFlow().end(context, output);
|
||||
flowSessions.removeLast();
|
||||
listeners.fireSessionEnded(context, session, output);
|
||||
if (hasEnded()) {
|
||||
this.outcome = new Event(this, session.getState().getId(), output);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.webflow.engine.impl;
|
||||
import org.springframework.binding.message.MessageContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
@@ -164,25 +163,15 @@ class RequestControlContextImpl implements RequestControlContext {
|
||||
// implementing RequestControlContext
|
||||
|
||||
public String getFlowExecutionUrl() {
|
||||
if (flowExecution.getKey() == null) {
|
||||
throw new IllegalStateException(
|
||||
"Flow execution key not yet assigned; unable to generate flow execution URL at this time");
|
||||
String key = flowExecution.getKey() != null ? flowExecution.getKey().toString() : null;
|
||||
if (key != null) {
|
||||
return externalContext.getFlowExecutionUri(flowExecution.getDefinition().getId(), key);
|
||||
} else {
|
||||
FlowExecutionRequestInfo requestInfo = new FlowExecutionRequestInfo(flowExecution.getFlowId(),
|
||||
flowExecution.getKey().toString());
|
||||
return externalContext.buildFlowExecutionUrl(requestInfo, true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFlowExecutionRedirect() {
|
||||
if (flowExecution.getKey() == null) {
|
||||
throw new IllegalStateException(
|
||||
"Flow execution key not yet assigned; unable to send a flow execution redirect request");
|
||||
} else {
|
||||
FlowExecutionRequestInfo requestInfo = new FlowExecutionRequestInfo(flowExecution.getFlowId(),
|
||||
flowExecution.getKey().toString());
|
||||
externalContext.sendFlowExecutionRedirect(requestInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentState(State state) {
|
||||
|
||||
@@ -16,23 +16,10 @@
|
||||
package org.springframework.webflow.execution;
|
||||
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
import org.springframework.webflow.execution.repository.FlowExecutionRepository;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
|
||||
/**
|
||||
* An execution of a flow definition. This is the central interface for manipulating a runtime execution of a flow
|
||||
* definition.
|
||||
*
|
||||
* A FlowExecution instance is typically created by a {@link FlowExecutionFactory factory}. A FlowExecution instance is
|
||||
* persisted using a {@link FlowExecutionRepository repository}. A FlowExecution's lifecycle is observed by zero or
|
||||
* more {@link FlowExecutionListener listeners}
|
||||
*
|
||||
* @see FlowDefinition
|
||||
* @see FlowSession
|
||||
* @see RequestContext
|
||||
* @see FlowExecutionListener
|
||||
* @see org.springframework.webflow.execution.repository.FlowExecutionRepository
|
||||
* @see org.springframework.webflow.executor.FlowExecutor
|
||||
* An execution of a flow definition. This is the central interface for manipulating a instance of a flow definition.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
@@ -44,11 +31,12 @@ public interface FlowExecution extends FlowExecutionContext {
|
||||
* <p>
|
||||
* When this method returns, execution status is either "paused" or "ended". If ended, the flow execution cannot be
|
||||
* used again. If "paused", the flow execution may be {@link #resume(ExternalContext)}.
|
||||
* @param input flow execution input
|
||||
* @param context the external context representing the calling environment
|
||||
* @throws FlowExecutionException if an exception was thrown within a state of the flow execution during request
|
||||
* processing
|
||||
*/
|
||||
public void start(ExternalContext context) throws FlowExecutionException;
|
||||
public void start(MutableAttributeMap input, ExternalContext context) throws FlowExecutionException;
|
||||
|
||||
/**
|
||||
* Resume this flow execution. May be called when the flow execution is paused.
|
||||
|
||||
@@ -74,6 +74,21 @@ public interface FlowExecutionContext {
|
||||
*/
|
||||
public boolean isActive();
|
||||
|
||||
/**
|
||||
* Returns a flag indicating if this execution has ended. A flow execution that has ended has been started but is no
|
||||
* longer active.
|
||||
* @see #hasStarted()
|
||||
* @see #isActive()
|
||||
* @return true if ended, false if not started or still active
|
||||
*/
|
||||
public boolean hasEnded();
|
||||
|
||||
/**
|
||||
* Returns the ending outcome event of this execution, or null if this execution has not yet ended.
|
||||
* @return the outcome event
|
||||
*/
|
||||
public Event getOutcome();
|
||||
|
||||
/**
|
||||
* Returns the active flow session of this flow execution. The active flow session is the currently executing
|
||||
* session -- it may be the "root flow" session, or it may be a subflow session if this flow execution has spawned a
|
||||
|
||||
@@ -123,10 +123,9 @@ public interface RequestContext {
|
||||
* constructs within that environment.
|
||||
* <p>
|
||||
* In addition, this context may be downcastable to a specific context type for a specific client environment, such
|
||||
* as a {@link org.springframework.webflow.context.servlet.ServletExternalContext} for servlets or a
|
||||
* {@link org.springframework.webflow.context.portlet.PortletExternalContext} for portlets. Such downcasting will
|
||||
* give you full access to a native HttpServletRequest, for example. With that said, for portability reasons you
|
||||
* should avoid coupling your flow artifacts to a specific deployment environment when possible.
|
||||
* as Servlets or Portlets. Such downcasting will give you full access to a native HttpServletRequest, for example.
|
||||
* With that said, for portability reasons you should avoid coupling your flow artifacts to a specific deployment
|
||||
* environment when possible.
|
||||
* @return the originating external context, the one that triggered the current execution request
|
||||
*/
|
||||
public ExternalContext getExternalContext();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package org.springframework.webflow.executor;
|
||||
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
|
||||
/**
|
||||
* The central facade and entry-point service interface into the Spring Web Flow system for <i>driving the executions of
|
||||
@@ -23,15 +25,27 @@ import org.springframework.webflow.context.ExternalContext;
|
||||
* clients.
|
||||
* <p>
|
||||
* Implementations of this interface abstract away much of the internal complexity of the web flow execution subsystem,
|
||||
* which consists of launching and resuming managed flow executions from repositories.
|
||||
* which consists of launching and resuming managed flow executions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface FlowExecutor {
|
||||
|
||||
/**
|
||||
* Execute the flow request initiated by the provided external context.
|
||||
* @param context the external context, representing a client environment calling into Spring Web Flow
|
||||
* Launch a new execution of the flow with the provided id.
|
||||
* @param flowId the flow definition identifier; should be unique among all top-level flow definitions (required).
|
||||
* @param input input to pass to the new execution on startup (optional)
|
||||
* @param context access to the calling environment (required)
|
||||
*/
|
||||
public void executeFlowRequest(ExternalContext context);
|
||||
public FlowExecutionResult launchExecution(String flowId, MutableAttributeMap input, ExternalContext context)
|
||||
throws FlowException;
|
||||
|
||||
/**
|
||||
* Resume the flow execution with the provided execution key.
|
||||
* @param flowId the id of the flow definition
|
||||
* @param flowExecutionKey the key of a paused execution of the flow definition
|
||||
* @param context access to the calling environment
|
||||
*/
|
||||
public FlowExecutionResult resumeExecution(String flowExecutionKey, ExternalContext context) throws FlowException;
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.webflow.executor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
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.FlowExecution;
|
||||
@@ -64,8 +65,8 @@ import org.springframework.webflow.execution.repository.FlowExecutionRepository;
|
||||
* @see FlowExecutionFactory
|
||||
* @see FlowExecutionRepository
|
||||
*
|
||||
* @author Erwin Vervaet
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
* @author Colin Sampaleanu
|
||||
*/
|
||||
public class FlowExecutorImpl implements FlowExecutor {
|
||||
@@ -101,59 +102,38 @@ public class FlowExecutorImpl implements FlowExecutor {
|
||||
this.executionRepository = executionRepository;
|
||||
}
|
||||
|
||||
public void executeFlowRequest(ExternalContext context) {
|
||||
if (context.getFlowExecutionKey() != null) {
|
||||
resumeExecution(context.getFlowExecutionKey(), context);
|
||||
public FlowExecutionResult launchExecution(String flowId, MutableAttributeMap input, ExternalContext context)
|
||||
throws FlowException {
|
||||
FlowDefinition flowDefinition = definitionLocator.getFlowDefinition(flowId);
|
||||
FlowExecution flowExecution = executionFactory.createFlowExecution(flowDefinition);
|
||||
flowExecution.start(input, context);
|
||||
if (!flowExecution.hasEnded()) {
|
||||
executionRepository.putFlowExecution(flowExecution);
|
||||
return FlowExecutionResult.createPausedResult(flowExecution.getDefinition().getId(), flowExecution.getKey()
|
||||
.toString());
|
||||
} else {
|
||||
launchExecution(context.getFlowId(), context);
|
||||
return FlowExecutionResult.createEndedResult(flowExecution.getDefinition().getId(), flowExecution
|
||||
.getOutcome());
|
||||
}
|
||||
}
|
||||
|
||||
private void launchExecution(String flowId, ExternalContext context) {
|
||||
public FlowExecutionResult resumeExecution(String flowExecutionKey, ExternalContext context) throws FlowException {
|
||||
FlowExecutionKey key = executionRepository.parseFlowExecutionKey(flowExecutionKey);
|
||||
FlowExecutionLock lock = executionRepository.getLock(key);
|
||||
try {
|
||||
FlowDefinition flowDefinition = definitionLocator.getFlowDefinition(flowId);
|
||||
FlowExecution flowExecution = executionFactory.createFlowExecution(flowDefinition);
|
||||
flowExecution.start(context);
|
||||
if (flowExecution.isActive()) {
|
||||
FlowExecution flowExecution = executionRepository.getFlowExecution(key);
|
||||
flowExecution.resume(context);
|
||||
if (!flowExecution.hasEnded()) {
|
||||
executionRepository.putFlowExecution(flowExecution);
|
||||
context.setPausedResult(flowExecution.getKey().toString());
|
||||
return FlowExecutionResult.createPausedResult(flowExecution.getDefinition().getId(), flowExecution
|
||||
.getKey().toString());
|
||||
} else {
|
||||
context.setEndedResult(null);
|
||||
}
|
||||
} catch (FlowException e) {
|
||||
if (!handleException(e, context)) {
|
||||
context.setExceptionResult(e);
|
||||
executionRepository.removeFlowExecution(flowExecution);
|
||||
return FlowExecutionResult.createEndedResult(flowExecution.getDefinition().getId(), flowExecution
|
||||
.getOutcome());
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void resumeExecution(String encodedKey, ExternalContext context) {
|
||||
try {
|
||||
FlowExecutionKey key = executionRepository.parseFlowExecutionKey(encodedKey);
|
||||
FlowExecutionLock lock = executionRepository.getLock(key);
|
||||
lock.lock();
|
||||
try {
|
||||
FlowExecution flowExecution = executionRepository.getFlowExecution(key);
|
||||
flowExecution.resume(context);
|
||||
if (flowExecution.isActive()) {
|
||||
executionRepository.putFlowExecution(flowExecution);
|
||||
context.setPausedResult(flowExecution.getKey().toString());
|
||||
} else {
|
||||
executionRepository.removeFlowExecution(flowExecution);
|
||||
context.setEndedResult(flowExecution.getKey().toString());
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} catch (FlowException e) {
|
||||
if (!handleException(e, context)) {
|
||||
context.setExceptionResult(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleException(FlowException e, ExternalContext context) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,23 @@
|
||||
package org.springframework.webflow.mvc;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.AbstractController;
|
||||
import org.springframework.webflow.context.servlet.DefaultFlowUrlHandler;
|
||||
import org.springframework.webflow.context.servlet.FlowUrlHandler;
|
||||
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.repository.NoSuchFlowExecutionException;
|
||||
import org.springframework.webflow.executor.FlowExecutionResult;
|
||||
import org.springframework.webflow.executor.FlowExecutor;
|
||||
|
||||
/**
|
||||
@@ -16,24 +28,156 @@ public class FlowController extends AbstractController {
|
||||
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
/**
|
||||
* Creates a new flow controller.
|
||||
*/
|
||||
public FlowController() {
|
||||
}
|
||||
private FlowUrlHandler urlHandler;
|
||||
|
||||
private Map flowHandlers = new HashMap();
|
||||
|
||||
/**
|
||||
* Sets the flow executor to delegate to. The FlowExecutor is the entry point into Spring Web Flow.
|
||||
* @param flowExecutor the flow executor
|
||||
* @param flowExecutor the web flow executor service
|
||||
*/
|
||||
public void setFlowExecutor(FlowExecutor flowExecutor) {
|
||||
public FlowController(FlowExecutor flowExecutor) {
|
||||
this.flowExecutor = flowExecutor;
|
||||
this.urlHandler = new DefaultFlowUrlHandler();
|
||||
// set the cache seconds property to 0 so no pages are cached by default for flows
|
||||
setCacheSeconds(0);
|
||||
}
|
||||
|
||||
public FlowUrlHandler getFlowUrlHandler() {
|
||||
return urlHandler;
|
||||
}
|
||||
|
||||
public void setFlowRequestUrlHandler(FlowUrlHandler urlHandler) {
|
||||
this.urlHandler = urlHandler;
|
||||
}
|
||||
|
||||
public void registerFlowHandler(FlowHandler handler) {
|
||||
flowHandlers.put(handler.getFlowId(), handler);
|
||||
}
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
ServletExternalContext context = new ServletExternalContext(getServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
|
||||
if (flowExecutionKey != null) {
|
||||
try {
|
||||
ServletExternalContext context = createServletExternalContext(request, response);
|
||||
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
|
||||
return handleFlowExecutionResult(result, context, request, response);
|
||||
} catch (FlowException e) {
|
||||
return handleFlowException(e, request, response);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
String flowId = urlHandler.getFlowId(request);
|
||||
MutableAttributeMap input = getFlowInput(flowId, request);
|
||||
ServletExternalContext context = createServletExternalContext(request, response);
|
||||
FlowExecutionResult result = flowExecutor.launchExecution(flowId, input, context);
|
||||
return handleFlowExecutionResult(result, context, request, response);
|
||||
} catch (FlowException e) {
|
||||
return handleFlowException(e, request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// subclassing hooks
|
||||
|
||||
protected ServletExternalContext createServletExternalContext(HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
return new ServletExternalContext(getServletContext(), request, response, urlHandler);
|
||||
}
|
||||
|
||||
protected MutableAttributeMap defaultFlowExecutionInputMap(HttpServletRequest request) {
|
||||
return new LocalAttributeMap(request.getParameterMap());
|
||||
}
|
||||
|
||||
protected ModelAndView defaultHandleFlowOutcome(String flowId, String outcome, AttributeMap endedOutput,
|
||||
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
// by default, just start the flow over passing the output as input
|
||||
response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, endedOutput, request));
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ModelAndView defaultHandleFlowException(String flowId, FlowException e, HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException {
|
||||
if (e instanceof NoSuchFlowExecutionException && flowId != null) {
|
||||
// by default, attempt to restart the flow
|
||||
response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, null, request));
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private ModelAndView handleFlowExecutionResult(FlowExecutionResult result, ServletExternalContext context,
|
||||
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
if (result.paused()) {
|
||||
if (context.flowExecutionRedirectRequested()) {
|
||||
String url = urlHandler.createFlowExecutionUrl(result.getFlowId(), result.getPausedKey(), request);
|
||||
response.sendRedirect(url);
|
||||
return null;
|
||||
} else if (context.externalRedirectRequested()) {
|
||||
response.sendRedirect(context.getExternalRedirectUrl());
|
||||
return null;
|
||||
} else {
|
||||
// nothing to do: flow has handled the response
|
||||
return null;
|
||||
}
|
||||
} else if (result.ended()) {
|
||||
if (context.flowDefinitionRedirectRequested()) {
|
||||
String flowId = context.getFlowRedirectFlowId();
|
||||
AttributeMap input = context.getFlowRedirectFlowInput();
|
||||
response.sendRedirect(urlHandler.createFlowDefinitionUrl(flowId, input, request));
|
||||
return null;
|
||||
} else if (context.externalRedirectRequested()) {
|
||||
response.sendRedirect(context.getExternalRedirectUrl());
|
||||
return null;
|
||||
} else {
|
||||
return handleFlowOutcome(result.getFlowId(), result.getEndedOutcome(), result.getEndedOutput(),
|
||||
request, response);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Execution result should have been one of [paused] or [ended]");
|
||||
}
|
||||
}
|
||||
|
||||
private MutableAttributeMap getFlowInput(String flowId, HttpServletRequest request) {
|
||||
FlowHandler handler = getFlowHandler(flowId);
|
||||
if (handler != null) {
|
||||
return handler.createExecutionInputMap(request);
|
||||
} else {
|
||||
return defaultFlowExecutionInputMap(request);
|
||||
}
|
||||
}
|
||||
|
||||
private ModelAndView handleFlowOutcome(String flowId, String outcome, AttributeMap endedOutput,
|
||||
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
FlowHandler handler = getFlowHandler(flowId);
|
||||
if (handler != null) {
|
||||
ModelAndView result = handler.handleExecutionOutcome(outcome, endedOutput, request, response);
|
||||
return result != null ? result : defaultHandleFlowOutcome(flowId, outcome, endedOutput, request, response);
|
||||
} else {
|
||||
return defaultHandleFlowOutcome(flowId, outcome, endedOutput, request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private ModelAndView handleFlowException(FlowException e, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String flowId = urlHandler.getFlowId(request);
|
||||
if (flowId != null) {
|
||||
FlowHandler handler = getFlowHandler(flowId);
|
||||
if (handler != null) {
|
||||
ModelAndView result = handler.handleException(e, request, response);
|
||||
return result != null ? result : defaultHandleFlowException(flowId, e, request, response);
|
||||
} else {
|
||||
return defaultHandleFlowException(flowId, e, request, response);
|
||||
}
|
||||
} else {
|
||||
return defaultHandleFlowException(null, e, request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private FlowHandler getFlowHandler(String flowId) {
|
||||
return (FlowHandler) flowHandlers.get(flowId);
|
||||
}
|
||||
}
|
||||
@@ -184,8 +184,8 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
|
||||
model.put("flowExecutionRequestContext", context);
|
||||
model.put("flowExecutionUrl", context.getFlowExecutionUrl());
|
||||
try {
|
||||
view.render(model, (HttpServletRequest) context.getExternalContext().getRequest(),
|
||||
(HttpServletResponse) context.getExternalContext().getResponse());
|
||||
view.render(model, (HttpServletRequest) context.getExternalContext().getNativeRequest(),
|
||||
(HttpServletResponse) context.getExternalContext().getNativeResponse());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Exception rendering view", e);
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package org.springframework.webflow.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||
import org.springframework.webflow.context.servlet.ServletExternalContext;
|
||||
import org.springframework.webflow.executor.FlowExecutor;
|
||||
|
||||
public class SpringWebServlet implements Servlet {
|
||||
|
||||
private ServletConfig config;
|
||||
|
||||
private ConfigurableWebApplicationContext container;
|
||||
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
this.config = config;
|
||||
container = new XmlWebApplicationContext();
|
||||
container.setConfigLocations(getConfigLocations(config));
|
||||
container.setServletConfig(config);
|
||||
container.setServletContext(config.getServletContext());
|
||||
container.refresh();
|
||||
this.flowExecutor = getFlowExecutor(container);
|
||||
}
|
||||
|
||||
public ServletConfig getServletConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public String getServletInfo() {
|
||||
return "Spring Web Servlet";
|
||||
}
|
||||
|
||||
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
|
||||
ServletContext servletContext = getServletConfig().getServletContext();
|
||||
new ServletExternalContext(servletContext, request, response).executeFlowRequest(flowExecutor);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
container.close();
|
||||
}
|
||||
|
||||
private String[] getConfigLocations(ServletConfig config) {
|
||||
String configLocations = config.getInitParameter("configLocations");
|
||||
if (configLocations != null) {
|
||||
return StringUtils.tokenizeToStringArray(config.getInitParameter("configLocations"),
|
||||
ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
} else {
|
||||
return new String[] { "/WEB-INF/webapp-config.xml" };
|
||||
}
|
||||
}
|
||||
|
||||
private FlowExecutor getFlowExecutor(WebApplicationContext container) {
|
||||
String[] beanNames = container.getBeanNamesForType(FlowExecutor.class);
|
||||
if (beanNames.length == 0) {
|
||||
throw new IllegalStateException("No bean of type FlowExecutor defined in context");
|
||||
} else if (beanNames.length > 1) {
|
||||
throw new IllegalStateException("More than one bean of type FlowExecutor defined in context.");
|
||||
} else {
|
||||
return (FlowExecutor) container.getBean(beanNames[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,14 @@
|
||||
*/
|
||||
package org.springframework.webflow.test;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.binding.collection.SharedMapDecorator;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
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.LocalSharedAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
@@ -33,20 +31,12 @@ import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
|
||||
/**
|
||||
* Mock implementation of the {@link ExternalContext} interface.
|
||||
*
|
||||
* @see ExternalContext
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MockExternalContext implements ExternalContext {
|
||||
|
||||
private String flowId;
|
||||
|
||||
private String flowExecutionKey;
|
||||
|
||||
private RequestPath requestPath;
|
||||
|
||||
private String requestMethod;
|
||||
private String contextPath;
|
||||
|
||||
private ParameterMap requestParameterMap = new MockParameterMap();
|
||||
|
||||
@@ -58,26 +48,26 @@ public class MockExternalContext implements ExternalContext {
|
||||
|
||||
private SharedAttributeMap applicationMap = new LocalSharedAttributeMap(new SharedMapDecorator(new HashMap()));
|
||||
|
||||
private Object context;
|
||||
private Object nativeContext = new Object();
|
||||
|
||||
private Object request;
|
||||
private Object nativeRequest = new Object();
|
||||
|
||||
private Object response;
|
||||
private Object nativeResponse = new Object();
|
||||
|
||||
private FlowDefinitionRequestInfo flowDefinitionRedirectResult;
|
||||
|
||||
private FlowExecutionRequestInfo flowExecutionRedirectResult;
|
||||
|
||||
private String externalRedirectResult;
|
||||
|
||||
private String pausedFlowExecutionKeyResult;
|
||||
|
||||
private FlowException exceptionResult;
|
||||
private StringWriter responseWriter = new StringWriter();
|
||||
|
||||
private boolean ajaxRequest;
|
||||
|
||||
private Map responseHeaders = new HashMap();
|
||||
|
||||
private boolean flowExecutionRedirectRequested;
|
||||
|
||||
private String flowDefinitionRedirectFlowId;
|
||||
|
||||
private AttributeMap flowDefinitionRedirectFlowInput;
|
||||
|
||||
private String externalRedirectUrl;
|
||||
|
||||
/**
|
||||
* Creates a mock external context with an empty request parameter map. Allows for bean style usage.
|
||||
*/
|
||||
@@ -97,20 +87,8 @@ public class MockExternalContext implements ExternalContext {
|
||||
|
||||
// implementing external context
|
||||
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public String getFlowExecutionKey() {
|
||||
return flowExecutionKey;
|
||||
}
|
||||
|
||||
public String getRequestMethod() {
|
||||
return requestMethod;
|
||||
}
|
||||
|
||||
public RequestPath getRequestPath() {
|
||||
return requestPath;
|
||||
public String getContextPath() {
|
||||
return contextPath;
|
||||
}
|
||||
|
||||
public ParameterMap getRequestParameterMap() {
|
||||
@@ -133,90 +111,57 @@ public class MockExternalContext implements ExternalContext {
|
||||
return applicationMap;
|
||||
}
|
||||
|
||||
public Object getContext() {
|
||||
return context;
|
||||
public Object getNativeContext() {
|
||||
return nativeContext;
|
||||
}
|
||||
|
||||
public Object getRequest() {
|
||||
return request;
|
||||
public Object getNativeRequest() {
|
||||
return nativeRequest;
|
||||
}
|
||||
|
||||
public Object getResponse() {
|
||||
return response;
|
||||
public Object getNativeResponse() {
|
||||
return nativeResponse;
|
||||
}
|
||||
|
||||
public PrintWriter getResponseWriter() {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
public boolean isAjaxRequest() {
|
||||
return ajaxRequest;
|
||||
}
|
||||
|
||||
public String encode(String string) {
|
||||
return string;
|
||||
public String getFlowExecutionUri(String flowId, String flowExecutionKey) {
|
||||
return "/" + flowId + "?execution=" + flowExecutionKey;
|
||||
}
|
||||
|
||||
public String buildFlowDefinitionUrl(FlowDefinitionRequestInfo requestInfo) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
public Writer getResponseWriter() {
|
||||
return responseWriter;
|
||||
}
|
||||
|
||||
public String buildFlowExecutionUrl(FlowExecutionRequestInfo requestInfo, boolean contextRelative) {
|
||||
return "/executions/" + requestInfo.getFlowDefinitionId() + "/" + requestInfo.getFlowExecutionKey();
|
||||
}
|
||||
|
||||
public void sendFlowDefinitionRedirect(FlowDefinitionRequestInfo requestInfo) {
|
||||
this.flowDefinitionRedirectResult = requestInfo;
|
||||
}
|
||||
|
||||
public void sendFlowExecutionRedirect(FlowExecutionRequestInfo requestInfo) {
|
||||
this.flowExecutionRedirectResult = requestInfo;
|
||||
}
|
||||
|
||||
public void sendExternalRedirect(String resourceUri) {
|
||||
externalRedirectResult = resourceUri;
|
||||
}
|
||||
|
||||
public void setPausedResult(String flowExecutionKey) {
|
||||
this.pausedFlowExecutionKeyResult = flowExecutionKey;
|
||||
}
|
||||
|
||||
public void setEndedResult(String flowExecutionKey) {
|
||||
|
||||
}
|
||||
|
||||
public void setExceptionResult(FlowException e) {
|
||||
exceptionResult = e;
|
||||
public void setResponseHeader(String name, String value) {
|
||||
this.responseHeaders.put(name, value);
|
||||
}
|
||||
|
||||
public boolean isResponseCommitted() {
|
||||
return false;
|
||||
return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested();
|
||||
}
|
||||
|
||||
// helper setters
|
||||
|
||||
public void setFlowId(String flowId) {
|
||||
this.flowId = flowId;
|
||||
public void requestFlowExecutionRedirect() {
|
||||
flowExecutionRedirectRequested = true;
|
||||
}
|
||||
|
||||
public void setFlowExecutionKey(String flowExecutionKey) {
|
||||
this.flowExecutionKey = flowExecutionKey;
|
||||
public void requestExternalRedirect(String uri) {
|
||||
externalRedirectUrl = uri;
|
||||
}
|
||||
|
||||
public void setRequestMethod(String requestMethod) {
|
||||
this.requestMethod = requestMethod;
|
||||
public void requestFlowDefinitionRedirect(String flowId, AttributeMap input) {
|
||||
flowDefinitionRedirectFlowId = flowId;
|
||||
flowDefinitionRedirectFlowInput = input;
|
||||
}
|
||||
|
||||
public void setRequestPath(RequestPath requestPath) {
|
||||
this.requestPath = requestPath;
|
||||
}
|
||||
|
||||
public void setContext(Object context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setRequest(Object request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public void setResponse(Object response) {
|
||||
this.response = response;
|
||||
/**
|
||||
* Set the context path of the application.
|
||||
* @param contextPath the context path
|
||||
*/
|
||||
public void setContextPath(String contextPath) {
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,6 +205,30 @@ public class MockExternalContext implements ExternalContext {
|
||||
this.applicationMap = applicationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the native context object.
|
||||
* @param nativeContext the native context
|
||||
*/
|
||||
public void setNativeContext(Object nativeContext) {
|
||||
this.nativeContext = nativeContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the native request object.
|
||||
* @param nativeRequest the native request object
|
||||
*/
|
||||
public void setNativeRequest(Object nativeRequest) {
|
||||
this.nativeRequest = nativeRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the native response object.
|
||||
* @param nativeResponse the native response object
|
||||
*/
|
||||
public void setNativeResponse(Object nativeResponse) {
|
||||
this.nativeResponse = nativeResponse;
|
||||
}
|
||||
|
||||
// convenience helpers
|
||||
|
||||
/**
|
||||
@@ -288,39 +257,45 @@ public class MockExternalContext implements ExternalContext {
|
||||
getMockRequestParameterMap().put(parameterName, parameterValues);
|
||||
}
|
||||
|
||||
public FlowDefinitionRequestInfo getFlowDefinitionRedirectResult() {
|
||||
return flowDefinitionRedirectResult;
|
||||
}
|
||||
|
||||
public FlowExecutionRequestInfo getFlowExecutionRedirectResult() {
|
||||
return flowExecutionRedirectResult;
|
||||
}
|
||||
|
||||
public String getExternalRedirectResult() {
|
||||
return externalRedirectResult;
|
||||
}
|
||||
|
||||
public String getPausedFlowExecutionKeyResult() {
|
||||
return pausedFlowExecutionKeyResult;
|
||||
}
|
||||
|
||||
public FlowException getExceptionResult() {
|
||||
return exceptionResult;
|
||||
}
|
||||
|
||||
public boolean isAjaxRequest() {
|
||||
return ajaxRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this request is an ajax request.
|
||||
* @param ajaxRequest true or false
|
||||
*/
|
||||
public void setAjaxRequest(boolean ajaxRequest) {
|
||||
this.ajaxRequest = ajaxRequest;
|
||||
}
|
||||
|
||||
public void setResponseHeader(String name, String value) {
|
||||
this.responseHeaders.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the response header entry
|
||||
* @param name the entry name
|
||||
* @return the entry value, or null if no entry was set with this name
|
||||
*/
|
||||
public String getResponseHeader(String name) {
|
||||
return (String) responseHeaders.get(name);
|
||||
}
|
||||
|
||||
public boolean flowExecutionRedirectRequested() {
|
||||
return flowExecutionRedirectRequested;
|
||||
}
|
||||
|
||||
public boolean flowDefinitionRedirectRequested() {
|
||||
return flowDefinitionRedirectFlowId != null;
|
||||
}
|
||||
|
||||
public String getFlowRedirectFlowId() {
|
||||
return flowDefinitionRedirectFlowId;
|
||||
}
|
||||
|
||||
public AttributeMap getFlowRedirectFlowInput() {
|
||||
return flowDefinitionRedirectFlowInput;
|
||||
}
|
||||
|
||||
public boolean externalRedirectRequested() {
|
||||
return externalRedirectUrl != null;
|
||||
}
|
||||
|
||||
public String getExternalRedirectUrl() {
|
||||
return externalRedirectUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ 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.FlowSession;
|
||||
@@ -45,6 +46,8 @@ public class MockFlowExecutionContext implements FlowExecutionContext {
|
||||
|
||||
private MutableAttributeMap attributes = new LocalAttributeMap();
|
||||
|
||||
private Event outcome;
|
||||
|
||||
/**
|
||||
* Creates a new mock flow execution context -- automatically installs a root flow definition and active flow
|
||||
* session.
|
||||
@@ -91,6 +94,10 @@ public class MockFlowExecutionContext implements FlowExecutionContext {
|
||||
return activeSession != null;
|
||||
}
|
||||
|
||||
public boolean hasEnded() {
|
||||
return hasStarted() && !isActive();
|
||||
}
|
||||
|
||||
public FlowSession getActiveSession() throws IllegalStateException {
|
||||
if (activeSession == null) {
|
||||
throw new IllegalStateException("No flow session is active");
|
||||
@@ -110,6 +117,10 @@ public class MockFlowExecutionContext implements FlowExecutionContext {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public Event getOutcome() {
|
||||
return outcome;
|
||||
}
|
||||
|
||||
// mutators
|
||||
|
||||
/**
|
||||
@@ -147,6 +158,14 @@ public class MockFlowExecutionContext implements FlowExecutionContext {
|
||||
this.conversationScope = scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result of this flow ending.
|
||||
* @param outcome the ending outcome event.
|
||||
*/
|
||||
public void setOutcome(Event outcome) {
|
||||
this.outcome = outcome;
|
||||
}
|
||||
|
||||
// convenience accessors
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,21 +19,19 @@ import org.springframework.webflow.execution.FlowExecutionKey;
|
||||
|
||||
/**
|
||||
* A simple flow execution key implementation. New instances of this class get their values from a sequence encapsulated
|
||||
* as a stativ private variable of this class.
|
||||
* as a static private variable of this class.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class MockFlowExecutionKey extends FlowExecutionKey {
|
||||
|
||||
private static int nextKey = 1;
|
||||
|
||||
private int value;
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Creates a new mock flow execution key.
|
||||
*/
|
||||
public MockFlowExecutionKey() {
|
||||
this.value = nextKey();
|
||||
public MockFlowExecutionKey(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
@@ -41,20 +39,14 @@ public class MockFlowExecutionKey extends FlowExecutionKey {
|
||||
return false;
|
||||
}
|
||||
MockFlowExecutionKey key = (MockFlowExecutionKey) o;
|
||||
return value == key.value;
|
||||
return value.equals(key.value);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return value * 29;
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
private static int nextKey() {
|
||||
int key = nextKey;
|
||||
nextKey++;
|
||||
return key;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,6 @@ import org.springframework.webflow.execution.FlowExecutionKeyFactory;
|
||||
*/
|
||||
public class MockFlowExecutionKeyFactory implements FlowExecutionKeyFactory {
|
||||
public FlowExecutionKey getKey(FlowExecution execution) {
|
||||
return new MockFlowExecutionKey();
|
||||
return new GeneratedFlowExecutionKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.springframework.binding.message.DefaultMessageContextFactory;
|
||||
import org.springframework.binding.message.MessageContext;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
@@ -173,9 +172,7 @@ public class MockRequestContext implements RequestContext {
|
||||
"Flow execution key not yet assigned; unable to build the flow execution url");
|
||||
} else {
|
||||
String flowDefinitionId = flowExecutionContext.getDefinition().getId();
|
||||
FlowExecutionRequestInfo requestInfo = new FlowExecutionRequestInfo(flowDefinitionId, flowExecutionContext
|
||||
.getKey().toString());
|
||||
return externalContext.buildFlowExecutionUrl(requestInfo, true);
|
||||
return externalContext.getFlowExecutionUri(flowDefinitionId, flowExecutionContext.getKey().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,10 +181,7 @@ public class MockRequestContext implements RequestContext {
|
||||
throw new IllegalStateException(
|
||||
"Flow execution key not yet assigned; unable to send a flow execution redirect request");
|
||||
} else {
|
||||
String flowDefinitionId = flowExecutionContext.getDefinition().getId();
|
||||
FlowExecutionRequestInfo requestInfo = new FlowExecutionRequestInfo(flowDefinitionId, flowExecutionContext
|
||||
.getKey().toString());
|
||||
externalContext.sendFlowExecutionRedirect(requestInfo);
|
||||
externalContext.requestFlowExecutionRedirect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public class MockRequestControlContext extends MockRequestContext implements Req
|
||||
}
|
||||
|
||||
public FlowExecutionKey assignFlowExecutionKey() {
|
||||
MockFlowExecutionKey key = new MockFlowExecutionKey();
|
||||
GeneratedFlowExecutionKey key = new GeneratedFlowExecutionKey();
|
||||
getMockFlowExecutionContext().setKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
|
||||
*/
|
||||
protected void startFlow(ExternalContext context) throws FlowExecutionException {
|
||||
flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());
|
||||
flowExecution.start(context);
|
||||
flowExecution.start(null, context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.binding.expression.support.StaticExpression;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ExternalRedirectActionTests extends TestCase {
|
||||
private ExternalRedirectAction action;
|
||||
|
||||
@@ -12,7 +12,7 @@ public class ExternalRedirectActionTests extends TestCase {
|
||||
action = new ExternalRedirectAction(new StaticExpression("/wherever"));
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
assertEquals("/wherever", context.getMockExternalContext().getExternalRedirectResult());
|
||||
assertEquals("/wherever", context.getMockExternalContext().getExternalRedirectUrl());
|
||||
}
|
||||
|
||||
public void testExecuteWithNullResourceUri() throws Exception {
|
||||
|
||||
@@ -7,7 +7,6 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.support.StaticExpression;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
public class FlowDefinitionRedirectActionTests extends TestCase {
|
||||
@@ -15,32 +14,26 @@ public class FlowDefinitionRedirectActionTests extends TestCase {
|
||||
|
||||
public void testExecute() throws Exception {
|
||||
Expression flowId = new StaticExpression("user");
|
||||
Expression[] requestElements = new Expression[] { new StaticExpression("1") };
|
||||
Map requestParameters = new HashMap();
|
||||
requestParameters.put(new StaticExpression("foo"), new StaticExpression("bar"));
|
||||
action = new FlowDefinitionRedirectAction(flowId, requestElements, requestParameters);
|
||||
Map input = new HashMap();
|
||||
input.put(new StaticExpression("foo"), new StaticExpression("bar"));
|
||||
action = new FlowDefinitionRedirectAction(flowId, input);
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
FlowDefinitionRequestInfo result = context.getMockExternalContext().getFlowDefinitionRedirectResult();
|
||||
assertEquals("user", result.getFlowDefinitionId());
|
||||
assertEquals("1", result.getRequestPath().getElement(0));
|
||||
assertEquals("bar", result.getRequestParameters().get("foo"));
|
||||
assertEquals("user", context.getMockExternalContext().getFlowRedirectFlowId());
|
||||
assertEquals("bar", context.getMockExternalContext().getFlowRedirectFlowInput().get("foo"));
|
||||
}
|
||||
|
||||
public void testExecuteWithNullRequestFields() throws Exception {
|
||||
Expression flowId = new StaticExpression("user");
|
||||
action = new FlowDefinitionRedirectAction(flowId, null, null);
|
||||
action = new FlowDefinitionRedirectAction(flowId, null);
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
FlowDefinitionRequestInfo result = context.getMockExternalContext().getFlowDefinitionRedirectResult();
|
||||
assertEquals("user", result.getFlowDefinitionId());
|
||||
assertEquals(null, result.getRequestPath());
|
||||
assertEquals(null, result.getRequestParameters());
|
||||
assertEquals("user", context.getMockExternalContext().getFlowRedirectFlowId());
|
||||
}
|
||||
|
||||
public void testExecuteWithNullFlowId() throws Exception {
|
||||
try {
|
||||
action = new FlowDefinitionRedirectAction(null, null, null);
|
||||
action = new FlowDefinitionRedirectAction(null, null);
|
||||
fail("Should have failed");
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ public class EnableScopesBeanDefinitionParserTests extends TestCase {
|
||||
|
||||
public void testExecute() {
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
executor.executeFlowRequest(context);
|
||||
}
|
||||
|
||||
public static class ConfigurationListener extends FlowExecutionListenerAdapter {
|
||||
|
||||
@@ -20,8 +20,6 @@ public class FlowExecutorBeanDefinitionParserTests extends TestCase {
|
||||
|
||||
public void testExecute() {
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
executor.executeFlowRequest(context);
|
||||
}
|
||||
|
||||
public static class ConfigurationListener extends FlowExecutionListenerAdapter {
|
||||
|
||||
@@ -50,8 +50,6 @@ public class FlowExecutorFactoryBeanTests extends TestCase {
|
||||
factoryBean.afterPropertiesSet();
|
||||
FlowExecutor executor = (FlowExecutor) factoryBean.getObject();
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
executor.executeFlowRequest(context);
|
||||
}
|
||||
|
||||
public void testGetFlowExecutorOptionsSpecified() throws Exception {
|
||||
@@ -78,11 +76,7 @@ public class FlowExecutorFactoryBeanTests extends TestCase {
|
||||
factoryBean.afterPropertiesSet();
|
||||
FlowExecutor executor = (FlowExecutor) factoryBean.getObject();
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
executor.executeFlowRequest(context);
|
||||
|
||||
MockExternalContext context2 = new MockExternalContext();
|
||||
context2.setFlowExecutionKey(context.getFlowExecutionKey());
|
||||
executor.executeFlowRequest(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class RequestPathTests extends TestCase {
|
||||
public void testNewPathParse() {
|
||||
RequestPath path = new RequestPath("/users/1");
|
||||
assertEquals(2, path.getElementCount());
|
||||
assertEquals("users", path.getElement(0));
|
||||
assertEquals("1", path.getElement(1));
|
||||
}
|
||||
|
||||
public void testNewPathParseTrailingSlash() {
|
||||
RequestPath path = new RequestPath("/users/1/");
|
||||
assertEquals(2, path.getElementCount());
|
||||
assertEquals("users", path.getElement(0));
|
||||
assertEquals("1", path.getElement(1));
|
||||
}
|
||||
|
||||
public void testNewPathParseNoLeadingSlash() {
|
||||
try {
|
||||
new RequestPath("users/1/");
|
||||
fail("should have failed");
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testOutOfBounds() {
|
||||
RequestPath path = new RequestPath("/users/1/");
|
||||
assertEquals(2, path.getElementCount());
|
||||
assertEquals("users", path.getElement(0));
|
||||
try {
|
||||
assertEquals("1", path.getElement(2));
|
||||
fail("should have failed");
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testEmptyPath() {
|
||||
RequestPath path = new RequestPath("/");
|
||||
assertEquals(1, path.getElementCount());
|
||||
assertEquals("", path.getElement(0));
|
||||
}
|
||||
|
||||
public void testSinglePathElement() {
|
||||
RequestPath path = new RequestPath("/users");
|
||||
assertEquals(1, path.getElementCount());
|
||||
assertEquals("users", path.getElement(0));
|
||||
}
|
||||
|
||||
public void testToString() {
|
||||
RequestPath path2 = new RequestPath("/users");
|
||||
RequestPath path3 = new RequestPath("/users/1/foo/bar");
|
||||
assertEquals("/users", path2.toString());
|
||||
assertEquals("/users/1/foo/bar", path3.toString());
|
||||
}
|
||||
|
||||
public void testPopElement() {
|
||||
RequestPath path = new RequestPath("/users/1");
|
||||
assertEquals(2, path.getElementCount());
|
||||
path = path.pop(1);
|
||||
assertEquals(1, path.getElementCount());
|
||||
assertEquals("/1", path.toString());
|
||||
}
|
||||
|
||||
public void testPopAllElements() {
|
||||
RequestPath path = new RequestPath("/users/1");
|
||||
assertEquals(2, path.getElementCount());
|
||||
path = path.pop(2);
|
||||
assertNull(path);
|
||||
}
|
||||
|
||||
public void testPopEmptyPath() {
|
||||
RequestPath path = new RequestPath("/");
|
||||
assertEquals(1, path.getElementCount());
|
||||
path = path.pop();
|
||||
assertNull(path);
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,6 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.ExternalContextHolder;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.executor.FlowExecutor;
|
||||
import org.springframework.webflow.test.MockParameterMap;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ServletExternalContext}.
|
||||
@@ -37,145 +29,15 @@ public class ServletExternalContextTests extends TestCase {
|
||||
|
||||
private MockHttpServletResponse response;
|
||||
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
private ServletExternalContext context;
|
||||
|
||||
protected void setUp() {
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
flowExecutor = new StubFlowExecutor();
|
||||
}
|
||||
|
||||
public void testProcessLaunchFlowRequest() throws Exception {
|
||||
request.setPathInfo("/booking");
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("booking", context.getFlowId());
|
||||
}
|
||||
public void testtest() {
|
||||
|
||||
public void testProcessLaunchFlowRequestTrailingSlash() throws Exception {
|
||||
request.setPathInfo("/booking/");
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("booking", context.getFlowId());
|
||||
}
|
||||
|
||||
public void testProcessLaunchFlowRequestElements() throws Exception {
|
||||
request.setPathInfo("/users/1");
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("users", context.getFlowId());
|
||||
assertEquals(context.getRequestPath().getElement(0), "1");
|
||||
}
|
||||
|
||||
public void testProcessLaunchFlowMultipleRequestElements() throws Exception {
|
||||
request.setPathInfo("/users/1/foo/bar//baz/");
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("users", context.getFlowId());
|
||||
assertEquals("1", context.getRequestPath().getElement(0));
|
||||
assertEquals("foo", context.getRequestPath().getElement(1));
|
||||
assertEquals("bar", context.getRequestPath().getElement(2));
|
||||
assertEquals("", context.getRequestPath().getElement(3));
|
||||
assertEquals("baz", context.getRequestPath().getElement(4));
|
||||
}
|
||||
|
||||
public void testProcessResumeFlowExecution() throws Exception {
|
||||
request.setPathInfo("/executions/booking/_c12345_k12345");
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("booking", context.getFlowId());
|
||||
assertEquals("_c12345_k12345", context.getFlowExecutionKey());
|
||||
}
|
||||
|
||||
public void testExternalContextUnbound() throws Exception {
|
||||
request.setPathInfo("/executions/booking/_c12345_k12345");
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
try {
|
||||
ExternalContextHolder.getExternalContext();
|
||||
fail("Should have failed");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testNoRequestPathInfo() {
|
||||
request.setPathInfo(null);
|
||||
try {
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
fail("Should have failed");
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testSendFlowExecutionRedirect() throws Exception {
|
||||
request.setPathInfo("/users/1");
|
||||
flowExecutor = new FlowExecutor() {
|
||||
public void executeFlowRequest(ExternalContext context) {
|
||||
context.sendFlowExecutionRedirect(new FlowExecutionRequestInfo("users", "_c12345_k12345"));
|
||||
}
|
||||
};
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("/executions/users/_c12345_k12345", response.getRedirectedUrl());
|
||||
}
|
||||
|
||||
public void testFlowExecutionRedirectAttemptOnEnd() throws Exception {
|
||||
request.setPathInfo("/users/1");
|
||||
flowExecutor = new FlowExecutor() {
|
||||
public void executeFlowRequest(ExternalContext context) {
|
||||
context.sendFlowExecutionRedirect(new FlowExecutionRequestInfo("users", "_c12345_k12345"));
|
||||
context.setEndedResult("_c12345_k12345");
|
||||
}
|
||||
};
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
try {
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
fail("Should have failed");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testSendFlowDefinitionRedirect() throws Exception {
|
||||
request.setPathInfo("/users/1");
|
||||
flowExecutor = new FlowExecutor() {
|
||||
public void executeFlowRequest(ExternalContext context) {
|
||||
MockParameterMap parameters = new MockParameterMap();
|
||||
parameters.put("foo", "bar");
|
||||
parameters.put("bar", "baz");
|
||||
RequestPath requestPath = new RequestPath("/1/you&me");
|
||||
FlowDefinitionRequestInfo requestInfo = new FlowDefinitionRequestInfo("customers", requestPath,
|
||||
parameters, "frag");
|
||||
context.sendFlowDefinitionRedirect(requestInfo);
|
||||
context.setEndedResult(null);
|
||||
}
|
||||
};
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("/customers/1/you%26me?foo=bar&bar=baz#frag", response.getRedirectedUrl());
|
||||
}
|
||||
|
||||
public void testSendExternalRedirect() throws Exception {
|
||||
request.setPathInfo("/users/1");
|
||||
flowExecutor = new FlowExecutor() {
|
||||
public void executeFlowRequest(ExternalContext context) {
|
||||
context.sendExternalRedirect("/foo/bar/baz");
|
||||
context.setEndedResult(null);
|
||||
}
|
||||
};
|
||||
context = new ServletExternalContext(new MockServletContext(), request, response);
|
||||
context.executeFlowRequest(flowExecutor);
|
||||
assertEquals("/foo/bar/baz", response.getRedirectedUrl());
|
||||
}
|
||||
|
||||
public class StubFlowExecutor implements FlowExecutor {
|
||||
public void executeFlowRequest(ExternalContext context) {
|
||||
assertNotNull(ExternalContextHolder.getExternalContext());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class ViewStateTests extends TestCase {
|
||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||
state.enter(context);
|
||||
assertFalse("Render called", context.getFlowScope().contains("renderCalled"));
|
||||
assertNotNull(context.getMockExternalContext().getFlowExecutionRedirectResult());
|
||||
assertTrue(context.getMockExternalContext().flowExecutionRedirectRequested());
|
||||
}
|
||||
|
||||
public void testEnterViewStateWithAlwaysRedirectOnPause() {
|
||||
@@ -62,7 +62,7 @@ public class ViewStateTests extends TestCase {
|
||||
context.setAlwaysRedirectOnPause(true);
|
||||
state.enter(context);
|
||||
assertFalse("Render called", context.getFlowScope().contains("renderCalled"));
|
||||
assertNotNull(context.getMockExternalContext().getFlowExecutionRedirectResult());
|
||||
assertTrue(context.getMockExternalContext().flowExecutionRedirectRequested());
|
||||
}
|
||||
|
||||
public void testResumeViewStateForRefresh() {
|
||||
|
||||
@@ -85,7 +85,7 @@ public class FlowExecutionImplFactoryTests extends TestCase {
|
||||
factory.setExecutionListenerLoader(new StaticFlowExecutionListenerLoader(listener1));
|
||||
FlowExecution execution = factory.createFlowExecution(flowDefinition);
|
||||
assertFalse(execution.isActive());
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
assertTrue(starting);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class FlowExecutionImplFactoryTests extends TestCase {
|
||||
}
|
||||
});
|
||||
FlowExecution execution = factory.createFlowExecution(flowDefinition);
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
assertTrue(getKeyCalled);
|
||||
assertNull(execution.getKey());
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ import org.springframework.webflow.execution.FlowSession;
|
||||
import org.springframework.webflow.execution.MockFlowExecutionListener;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
import org.springframework.webflow.test.GeneratedFlowExecutionKey;
|
||||
import org.springframework.webflow.test.MockExternalContext;
|
||||
import org.springframework.webflow.test.MockFlowExecutionKey;
|
||||
|
||||
/**
|
||||
* General flow execution tests.
|
||||
@@ -59,7 +59,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setMessageContextFactory(new DefaultMessageContextFactory(new StaticMessageSource()));
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
assertFalse(execution.hasStarted());
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
assertTrue(execution.hasStarted());
|
||||
assertFalse(execution.isActive());
|
||||
assertEquals(1, mockListener.getRequestsSubmittedCount());
|
||||
@@ -91,7 +91,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setListeners(listeners);
|
||||
execution.setMessageContextFactory(new DefaultMessageContextFactory(new StaticMessageSource()));
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
assertTrue(execution.isActive());
|
||||
assertEquals(1, mockListener.getPausedCount());
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
assertFalse(execution.hasStarted());
|
||||
try {
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
fail("Should have failed");
|
||||
} catch (FlowExecutionException e) {
|
||||
assertEquals(flow.getId(), e.getFlowId());
|
||||
@@ -131,7 +131,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
assertFalse(execution.hasStarted());
|
||||
try {
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
fail("Should have failed");
|
||||
} catch (FlowExecutionException e) {
|
||||
assertEquals(flow.getId(), e.getFlowId());
|
||||
@@ -152,7 +152,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
assertFalse(execution.hasStarted());
|
||||
try {
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
fail("Should have failed");
|
||||
} catch (FlowExecutionException ex) {
|
||||
assertSame(e, ex);
|
||||
@@ -165,9 +165,9 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
FlowExecutionImpl execution = new FlowExecutionImpl(flow);
|
||||
execution.setMessageContextFactory(new DefaultMessageContextFactory(new StaticMessageSource()));
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
try {
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
fail("Should've failed");
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
@@ -184,7 +184,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setListeners(listeners);
|
||||
execution.setKeyFactory(new MockFlowExecutionKeyFactory());
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
context = new MockExternalContext();
|
||||
execution.resume(context);
|
||||
assertEquals(1, mockListener.getResumingCount());
|
||||
@@ -204,7 +204,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setMessageContextFactory(new DefaultMessageContextFactory(new StaticMessageSource()));
|
||||
execution.setListeners(listeners);
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
context = new MockExternalContext();
|
||||
try {
|
||||
execution.resume(context);
|
||||
@@ -221,7 +221,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
FlowExecutionImpl execution = new FlowExecutionImpl(flow);
|
||||
execution.setMessageContextFactory(new DefaultMessageContextFactory(new StaticMessageSource()));
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
try {
|
||||
execution.resume(context);
|
||||
fail("Should've failed");
|
||||
@@ -244,7 +244,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setListeners(listeners);
|
||||
execution.setKeyFactory(new MockFlowExecutionKeyFactory());
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
context = new MockExternalContext();
|
||||
try {
|
||||
execution.resume(context);
|
||||
@@ -270,7 +270,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setListeners(listeners);
|
||||
execution.setKeyFactory(new MockFlowExecutionKeyFactory());
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
context = new MockExternalContext();
|
||||
try {
|
||||
execution.resume(context);
|
||||
@@ -294,7 +294,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
execution.setKeyFactory(new MockFlowExecutionKeyFactory());
|
||||
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
execution.start(context);
|
||||
execution.start(null, context);
|
||||
assertNull("RequestContext was not released", RequestContextHolder.getRequestContext());
|
||||
|
||||
context = new MockExternalContext();
|
||||
@@ -305,7 +305,7 @@ public class FlowExecutionImplTests extends TestCase {
|
||||
|
||||
private static class MockFlowExecutionKeyFactory implements FlowExecutionKeyFactory {
|
||||
public FlowExecutionKey getKey(FlowExecution execution) {
|
||||
return new MockFlowExecutionKey();
|
||||
return new GeneratedFlowExecutionKey();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.springframework.webflow.execution.FlowExecutionListener;
|
||||
import org.springframework.webflow.execution.MockFlowExecutionListener;
|
||||
import org.springframework.webflow.execution.factory.FlowExecutionListenerLoader;
|
||||
import org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader;
|
||||
import org.springframework.webflow.test.MockFlowExecutionKey;
|
||||
import org.springframework.webflow.test.GeneratedFlowExecutionKey;
|
||||
|
||||
public class FlowExecutionStateRestorerImplTests extends TestCase {
|
||||
private SimpleFlowDefinitionLocator definitionLocator;
|
||||
@@ -25,7 +25,7 @@ public class FlowExecutionStateRestorerImplTests extends TestCase {
|
||||
private LocalAttributeMap executionAttributes = new LocalAttributeMap();
|
||||
private FlowExecutionListener listener = new MockFlowExecutionListener();
|
||||
private FlowExecutionListenerLoader executionListenerLoader = new StaticFlowExecutionListenerLoader(listener);
|
||||
MockFlowExecutionKey newKey = new MockFlowExecutionKey();
|
||||
GeneratedFlowExecutionKey newKey = new GeneratedFlowExecutionKey();
|
||||
private FlowExecutionKeyFactory executionKeyFactory = new FlowExecutionKeyFactory() {
|
||||
public FlowExecutionKey getKey(FlowExecution execution) {
|
||||
return newKey;
|
||||
@@ -40,7 +40,7 @@ public class FlowExecutionStateRestorerImplTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testRestoreStateNoSessions() {
|
||||
FlowExecutionKey key = new MockFlowExecutionKey();
|
||||
FlowExecutionKey key = new GeneratedFlowExecutionKey();
|
||||
LocalAttributeMap conversationScope = new LocalAttributeMap();
|
||||
FlowExecutionImpl execution = new FlowExecutionImpl("parent", new LinkedList());
|
||||
stateRestorer.restoreState(execution, key, conversationScope, executionKeyFactory);
|
||||
@@ -55,7 +55,7 @@ public class FlowExecutionStateRestorerImplTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testRestoreStateFlowDefinitionIdNotSet() {
|
||||
FlowExecutionKey key = new MockFlowExecutionKey();
|
||||
FlowExecutionKey key = new GeneratedFlowExecutionKey();
|
||||
LocalAttributeMap conversationScope = new LocalAttributeMap();
|
||||
FlowExecutionImpl execution = new FlowExecutionImpl();
|
||||
try {
|
||||
@@ -67,7 +67,7 @@ public class FlowExecutionStateRestorerImplTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testRestoreStateFlowSessionsNotSet() {
|
||||
FlowExecutionKey key = new MockFlowExecutionKey();
|
||||
FlowExecutionKey key = new GeneratedFlowExecutionKey();
|
||||
LocalAttributeMap conversationScope = new LocalAttributeMap();
|
||||
FlowExecutionImpl execution = new FlowExecutionImpl("parent", null);
|
||||
try {
|
||||
|
||||
@@ -95,7 +95,7 @@ public class TransitionExecutingFlowExecutionExceptionHandlerTests extends TestC
|
||||
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
|
||||
factory.setExecutionListenerLoader(new StaticFlowExecutionListenerLoader(listener));
|
||||
FlowExecution execution = factory.createFlowExecution(flow);
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
assertTrue("Should have ended", !execution.isActive());
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public class TransitionExecutingFlowExecutionExceptionHandlerTests extends TestC
|
||||
flow.getExceptionHandlerSet().add(handler);
|
||||
FlowExecution execution = new FlowExecutionImplFactory().createFlowExecution(flow);
|
||||
try {
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
fail("Should have failed no such state");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class TransitionExecutingFlowExecutionExceptionHandlerTests extends TestC
|
||||
public void testStateExceptionHandlingRethrow() {
|
||||
FlowExecution execution = new FlowExecutionImplFactory().createFlowExecution(flow);
|
||||
try {
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
fail("Should have rethrown");
|
||||
} catch (FlowExecutionException e) {
|
||||
// expected
|
||||
@@ -147,7 +147,7 @@ public class TransitionExecutingFlowExecutionExceptionHandlerTests extends TestC
|
||||
};
|
||||
Flow flow = new FlowAssembler(builder, new MockFlowBuilderContext("flow")).assembleFlow();
|
||||
FlowExecution execution = new FlowExecutionImplFactory().createFlowExecution(flow);
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
assertTrue(execution.isActive());
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class SerializedFlowExecutionContinuationFactoryTests extends TestCase {
|
||||
|
||||
public void testCreateContinuation() {
|
||||
FlowExecution flowExecution = new FlowExecutionImplFactory().createFlowExecution(flow);
|
||||
flowExecution.start(new MockExternalContext());
|
||||
flowExecution.start(null, new MockExternalContext());
|
||||
flowExecution.getActiveSession().getScope().put("foo", "bar");
|
||||
FlowExecutionContinuation continuation = factory.createContinuation(flowExecution);
|
||||
FlowExecutionImpl flowExecution2 = (FlowExecutionImpl) continuation.unmarshal();
|
||||
@@ -58,7 +58,7 @@ public class SerializedFlowExecutionContinuationFactoryTests extends TestCase {
|
||||
|
||||
public void testRestoreContinuation() {
|
||||
FlowExecution flowExecution = new FlowExecutionImplFactory().createFlowExecution(flow);
|
||||
flowExecution.start(new MockExternalContext());
|
||||
flowExecution.start(null, new MockExternalContext());
|
||||
flowExecution.getActiveSession().getScope().put("foo", "bar");
|
||||
FlowExecutionContinuation continuation = factory.createContinuation(flowExecution);
|
||||
byte[] bytes = continuation.toByteArray();
|
||||
|
||||
@@ -90,7 +90,7 @@ public class DefaultFlowExecutionRepositoryTests extends TestCase {
|
||||
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
|
||||
factory.setExecutionKeyFactory(repository);
|
||||
FlowExecution execution = factory.createFlowExecution(flow);
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
assertNotNull(execution.getKey());
|
||||
repository.putFlowExecution(execution);
|
||||
FlowExecution execution2 = repository.getFlowExecution(execution.getKey());
|
||||
@@ -113,7 +113,7 @@ public class DefaultFlowExecutionRepositoryTests extends TestCase {
|
||||
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
|
||||
factory.setExecutionKeyFactory(repository);
|
||||
FlowExecution execution = factory.createFlowExecution(flow);
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
assertNotNull(execution.getKey());
|
||||
repository.putFlowExecution(execution);
|
||||
repository.removeFlowExecution(execution);
|
||||
@@ -140,7 +140,7 @@ public class DefaultFlowExecutionRepositoryTests extends TestCase {
|
||||
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
|
||||
factory.setExecutionKeyFactory(repository);
|
||||
FlowExecution execution = factory.createFlowExecution(flow);
|
||||
execution.start(new MockExternalContext());
|
||||
execution.start(null, new MockExternalContext());
|
||||
try {
|
||||
repository.removeFlowExecution(execution);
|
||||
repository.removeFlowExecution(execution);
|
||||
|
||||
@@ -2,96 +2,182 @@ package org.springframework.webflow.executor;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.webflow.context.ExternalContextHolder;
|
||||
import org.springframework.webflow.conversation.impl.SessionBindingConversationManager;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
|
||||
import org.springframework.webflow.engine.EndState;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.RequestControlContext;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.StubViewFactory;
|
||||
import org.springframework.webflow.engine.ViewState;
|
||||
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
|
||||
import org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository;
|
||||
import org.easymock.EasyMock;
|
||||
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.repository.FlowExecutionLock;
|
||||
import org.springframework.webflow.execution.repository.FlowExecutionRepository;
|
||||
import org.springframework.webflow.test.GeneratedFlowExecutionKey;
|
||||
import org.springframework.webflow.test.MockExternalContext;
|
||||
import org.springframework.webflow.test.MockFlowExecutionKey;
|
||||
|
||||
public class FlowExecutorImplTests extends TestCase {
|
||||
private FlowDefinitionRegistryImpl definitionLocator;
|
||||
private FlowExecutionImplFactory executionFactory;
|
||||
private DefaultFlowExecutionRepository executionRepository;
|
||||
private FlowExecutorImpl executor;
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
// mocks
|
||||
private FlowDefinitionLocator locator;
|
||||
private FlowDefinition definition;
|
||||
private FlowExecutionFactory factory;
|
||||
private FlowExecution execution;
|
||||
private FlowExecutionRepository repository;
|
||||
private FlowExecutionLock lock;
|
||||
|
||||
protected void setUp() {
|
||||
definitionLocator = new FlowDefinitionRegistryImpl();
|
||||
executionFactory = new FlowExecutionImplFactory();
|
||||
executionRepository = new DefaultFlowExecutionRepository(new SessionBindingConversationManager(),
|
||||
new FlowExecutionImplStateRestorer(definitionLocator));
|
||||
executionFactory.setExecutionKeyFactory(executionRepository);
|
||||
executor = new FlowExecutorImpl(definitionLocator, executionFactory, executionRepository);
|
||||
locator = (FlowDefinitionLocator) EasyMock.createMock(FlowDefinitionLocator.class);
|
||||
definition = (FlowDefinition) EasyMock.createMock(FlowDefinition.class);
|
||||
factory = (FlowExecutionFactory) EasyMock.createMock(FlowExecutionFactory.class);
|
||||
execution = (FlowExecution) EasyMock.createMock(FlowExecution.class);
|
||||
repository = (FlowExecutionRepository) EasyMock.createMock(FlowExecutionRepository.class);
|
||||
lock = (FlowExecutionLock) EasyMock.createMock(FlowExecutionLock.class);
|
||||
|
||||
flowExecutor = new FlowExecutorImpl(locator, factory, repository);
|
||||
}
|
||||
|
||||
public void testLaunchAndEnd() {
|
||||
Flow flow = new Flow("flow");
|
||||
new EndState(flow, "end");
|
||||
definitionLocator.registerFlowDefinition(flow);
|
||||
public void testLaunchFlowExecution() {
|
||||
String flowId = "foo";
|
||||
MutableAttributeMap input = null;
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
|
||||
ExternalContextHolder.setExternalContext(context);
|
||||
executor.executeFlowRequest(context);
|
||||
ExternalContextHolder.setExternalContext(null);
|
||||
EasyMock.expect(locator.getFlowDefinition(flowId)).andReturn(definition);
|
||||
EasyMock.expect(factory.createFlowExecution(definition)).andReturn(execution);
|
||||
|
||||
assertNull(context.getFlowExecutionRedirectResult());
|
||||
assertNull(context.getPausedFlowExecutionKeyResult());
|
||||
assertNull(context.getExceptionResult());
|
||||
execution.start(input, context);
|
||||
|
||||
execution.hasEnded();
|
||||
EasyMock.expectLastCall().andReturn(Boolean.FALSE);
|
||||
|
||||
repository.putFlowExecution(execution);
|
||||
|
||||
EasyMock.expect(execution.getDefinition()).andReturn(definition);
|
||||
EasyMock.expect(definition.getId()).andReturn("foo");
|
||||
EasyMock.expect(execution.getKey()).andReturn(new MockFlowExecutionKey("12345"));
|
||||
|
||||
replayMocks();
|
||||
|
||||
FlowExecutionResult result = flowExecutor.launchExecution("foo", null, context);
|
||||
assertTrue(result.paused());
|
||||
assertEquals("12345", result.getPausedKey());
|
||||
assertFalse(result.ended());
|
||||
assertNull(result.getEndedOutcome());
|
||||
assertNull(result.getEndedOutput());
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
public void testLaunchAndResume() {
|
||||
Flow flow = new Flow("flow");
|
||||
new ViewState(flow, "pause", new StubViewFactory());
|
||||
definitionLocator.registerFlowDefinition(flow);
|
||||
public void testLaunchFlowExecutionEndsAfterProcessing() {
|
||||
String flowId = "foo";
|
||||
MutableAttributeMap input = null;
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
|
||||
ExternalContextHolder.setExternalContext(context);
|
||||
executor.executeFlowRequest(context);
|
||||
ExternalContextHolder.setExternalContext(null);
|
||||
EasyMock.expect(locator.getFlowDefinition(flowId)).andReturn(definition);
|
||||
EasyMock.expect(factory.createFlowExecution(definition)).andReturn(execution);
|
||||
|
||||
assertNotNull(context.getPausedFlowExecutionKeyResult());
|
||||
assertNull(context.getExceptionResult());
|
||||
assertNull(context.getFlowExecutionRedirectResult());
|
||||
execution.start(input, context);
|
||||
|
||||
MockExternalContext context2 = new MockExternalContext();
|
||||
context2.setSessionMap(context.getSessionMap());
|
||||
context2.setFlowId("flow");
|
||||
context2.setFlowExecutionKey(context.getPausedFlowExecutionKeyResult());
|
||||
execution.hasEnded();
|
||||
EasyMock.expectLastCall().andReturn(Boolean.TRUE);
|
||||
|
||||
ExternalContextHolder.setExternalContext(context);
|
||||
executor.executeFlowRequest(context2);
|
||||
ExternalContextHolder.setExternalContext(null);
|
||||
EasyMock.expect(execution.getDefinition()).andReturn(definition);
|
||||
EasyMock.expect(definition.getId()).andReturn("foo");
|
||||
EasyMock.expect(execution.getOutcome()).andReturn(new Event(execution, "finish", null));
|
||||
|
||||
replayMocks();
|
||||
|
||||
FlowExecutionResult result = flowExecutor.launchExecution("foo", null, context);
|
||||
assertTrue(result.ended());
|
||||
assertEquals("finish", result.getEndedOutcome());
|
||||
assertTrue(result.getEndedOutput().isEmpty());
|
||||
assertFalse(result.paused());
|
||||
assertNull(result.getPausedKey());
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
public void testLaunchAndException() {
|
||||
Flow flow = new Flow("flow");
|
||||
final UnsupportedOperationException e = new UnsupportedOperationException();
|
||||
new State(flow, "exception") {
|
||||
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
definitionLocator.registerFlowDefinition(flow);
|
||||
public void testResumeFlowExecution() {
|
||||
String flowExecutionKey = "12345";
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
context.setFlowId("flow");
|
||||
FlowExecutionKey key = new GeneratedFlowExecutionKey();
|
||||
|
||||
ExternalContextHolder.setExternalContext(context);
|
||||
executor.executeFlowRequest(context);
|
||||
ExternalContextHolder.setExternalContext(null);
|
||||
EasyMock.expect(repository.parseFlowExecutionKey(flowExecutionKey)).andReturn(key);
|
||||
EasyMock.expect(repository.getLock(key)).andReturn(lock);
|
||||
|
||||
assertNull(context.getFlowExecutionRedirectResult());
|
||||
assertNull(context.getPausedFlowExecutionKeyResult());
|
||||
assertNotNull(context.getExceptionResult());
|
||||
assertSame(e, context.getExceptionResult().getCause());
|
||||
lock.lock();
|
||||
EasyMock.expect(repository.getFlowExecution(key)).andReturn(execution);
|
||||
|
||||
execution.resume(context);
|
||||
|
||||
execution.hasEnded();
|
||||
EasyMock.expectLastCall().andReturn(Boolean.FALSE);
|
||||
|
||||
repository.putFlowExecution(execution);
|
||||
|
||||
EasyMock.expect(execution.getDefinition()).andReturn(definition);
|
||||
EasyMock.expect(definition.getId()).andReturn("foo");
|
||||
EasyMock.expect(execution.getKey()).andReturn(new MockFlowExecutionKey("12345"));
|
||||
|
||||
lock.unlock();
|
||||
|
||||
replayMocks();
|
||||
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
|
||||
verifyMocks();
|
||||
|
||||
assertTrue(result.paused());
|
||||
assertEquals("12345", result.getPausedKey());
|
||||
assertFalse(result.ended());
|
||||
assertNull(result.getEndedOutcome());
|
||||
assertNull(result.getEndedOutput());
|
||||
verifyMocks();
|
||||
|
||||
}
|
||||
|
||||
public void testResumeFlowExecutionEndsAfterProcessing() {
|
||||
String flowExecutionKey = "12345";
|
||||
MockExternalContext context = new MockExternalContext();
|
||||
FlowExecutionKey key = new MockFlowExecutionKey("12345");
|
||||
|
||||
EasyMock.expect(repository.parseFlowExecutionKey(flowExecutionKey)).andReturn(key);
|
||||
EasyMock.expect(repository.getLock(key)).andReturn(lock);
|
||||
|
||||
lock.lock();
|
||||
EasyMock.expect(repository.getFlowExecution(key)).andReturn(execution);
|
||||
|
||||
execution.resume(context);
|
||||
|
||||
execution.hasEnded();
|
||||
EasyMock.expectLastCall().andReturn(Boolean.TRUE);
|
||||
|
||||
EasyMock.expect(execution.getDefinition()).andReturn(definition);
|
||||
EasyMock.expect(definition.getId()).andReturn("foo");
|
||||
|
||||
LocalAttributeMap output = new LocalAttributeMap();
|
||||
output.put("foo", "bar");
|
||||
EasyMock.expect(execution.getOutcome()).andReturn(new Event(execution, "finish", output));
|
||||
|
||||
repository.removeFlowExecution(execution);
|
||||
|
||||
lock.unlock();
|
||||
|
||||
replayMocks();
|
||||
|
||||
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
|
||||
assertTrue(result.ended());
|
||||
assertEquals("finish", result.getEndedOutcome());
|
||||
assertEquals(output, result.getEndedOutput());
|
||||
assertFalse(result.paused());
|
||||
assertNull(result.getPausedKey());
|
||||
|
||||
verifyMocks();
|
||||
}
|
||||
|
||||
private void replayMocks() {
|
||||
EasyMock.replay(new Object[] { locator, definition, factory, execution, repository });
|
||||
}
|
||||
|
||||
private void verifyMocks() {
|
||||
EasyMock.verify(new Object[] { locator, definition, factory, execution, repository });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.View;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
import org.springframework.webflow.test.GeneratedFlowExecutionKey;
|
||||
import org.springframework.webflow.test.MockExternalContext;
|
||||
import org.springframework.webflow.test.MockFlowExecutionKey;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
public class MvcViewFactoryTests extends TestCase {
|
||||
@@ -55,10 +55,10 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
MockExternalContext externalContext = new MockExternalContext();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
externalContext.setRequest(request);
|
||||
externalContext.setResponse(response);
|
||||
externalContext.setNativeRequest(request);
|
||||
externalContext.setNativeResponse(response);
|
||||
context.setExternalContext(externalContext);
|
||||
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey());
|
||||
context.getMockFlowExecutionContext().setKey(new GeneratedFlowExecutionKey());
|
||||
View view = viewFactory.getView(context);
|
||||
assertEquals(false, view.eventSignaled());
|
||||
view.render();
|
||||
@@ -75,10 +75,10 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
MockExternalContext externalContext = new MockExternalContext();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
externalContext.setRequest(request);
|
||||
externalContext.setResponse(response);
|
||||
externalContext.setNativeRequest(request);
|
||||
externalContext.setNativeResponse(response);
|
||||
context.setExternalContext(externalContext);
|
||||
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey());
|
||||
context.getMockFlowExecutionContext().setKey(new GeneratedFlowExecutionKey());
|
||||
View view = viewFactory.getView(context);
|
||||
assertEquals(false, view.eventSignaled());
|
||||
view.render();
|
||||
@@ -103,10 +103,10 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
externalContext.putRequestParameter("_eventId", "foo");
|
||||
externalContext.setRequest(request);
|
||||
externalContext.setResponse(response);
|
||||
externalContext.setNativeRequest(request);
|
||||
externalContext.setNativeResponse(response);
|
||||
context.setExternalContext(externalContext);
|
||||
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey());
|
||||
context.getMockFlowExecutionContext().setKey(new GeneratedFlowExecutionKey());
|
||||
View view = viewFactory.getView(context);
|
||||
assertEquals(true, view.eventSignaled());
|
||||
Event e = view.getEvent();
|
||||
@@ -134,10 +134,10 @@ public class MvcViewFactoryTests extends TestCase {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
externalContext.putRequestParameter("_eventId_foo", "true");
|
||||
externalContext.setRequest(request);
|
||||
externalContext.setResponse(response);
|
||||
externalContext.setNativeRequest(request);
|
||||
externalContext.setNativeResponse(response);
|
||||
context.setExternalContext(externalContext);
|
||||
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey());
|
||||
context.getMockFlowExecutionContext().setKey(new GeneratedFlowExecutionKey());
|
||||
View view = viewFactory.getView(context);
|
||||
assertEquals(true, view.eventSignaled());
|
||||
Event e = view.getEvent();
|
||||
|
||||
Reference in New Issue
Block a user