Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add option to save Web Flow output to MVC flash scope

Output from a flow can now be saved to the flash scope of Spring MVC
when the 'saveOutputToFlashScopeOnRedirect' property of the
FlowHandlerAdapter is 'true'.

For backwards compatibility this is an opt-in feature that must be
configured by the user.

Issues: SWF-1561
  • Loading branch information...
commit 91e8a96c11670aec987cdcb09115479614c0ab15 1 parent d066f1f
Phil Webb philwebb authored Rossen Stoyanchev committed
90 spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java
View
@@ -27,9 +27,14 @@
import org.springframework.js.ajax.AjaxHandler;
import org.springframework.js.ajax.SpringJavascriptAjaxHandler;
import org.springframework.util.Assert;
+import org.springframework.web.servlet.FlashMap;
+import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.support.WebContentGenerator;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.webflow.context.servlet.DefaultFlowUrlHandler;
import org.springframework.webflow.context.servlet.FlowUrlHandler;
import org.springframework.webflow.context.servlet.ServletExternalContext;
@@ -45,8 +50,9 @@
* A custom MVC HandlerAdapter that encapsulates the generic workflow associated with executing flows in a Servlet
* environment. Delegates to mapped {@link FlowHandler flow handlers} to manage the interaction with executions of
* specific flow definitions.
- *
+ *
* @author Keith Donald
+ * @author Phillip Webb
*/
public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAdapter, InitializingBean {
@@ -77,6 +83,8 @@
private boolean redirectHttp10Compatible = true;
+ private boolean saveOutputToFlashScopeOnRedirect;
+
/**
* Creates a new flow handler adapter.
* @see #setFlowExecutor(FlowExecutor)
@@ -157,6 +165,32 @@ public void setRedirectHttp10Compatible(boolean redirectHttp10Compatible) {
this.redirectHttp10Compatible = redirectHttp10Compatible;
}
+ /**
+ * Set whether servlet relative redirects sent by this handler adapter
+ * should pass {@link FlowExecutionOutcome#getOutput() flow output} to the
+ * Spring MVC {@link FlashMap flash scope}.
+ *
+ * <p>By default, to remain compatible with previous releases, flow output is
+ * not mapped to flash scope.
+ *
+ * @param saveOutputToFlashScopeOnRedirect
+ * @see #getFlashOutput(HttpServletRequest, HttpServletResponse, FlowExecutionResult)
+ */
+ public void setSaveOutputToFlashScopeOnRedirect(boolean saveOutputToFlashScopeOnRedirect) {
+ this.saveOutputToFlashScopeOnRedirect = saveOutputToFlashScopeOnRedirect;
+ }
+
+ /**
+ * Whether servlet relative redirects should pass
+ * {@link FlowExecutionOutcome#getOutput() flow output} to the Spring MVC
+ * {@link FlashMap flash scope}.
+ *
+ * @return {@code true} if so, {@code false} otherwise
+ */
+ public boolean getSaveOutputToFlashScopeOnRedirect() {
+ return this.saveOutputToFlashScopeOnRedirect;
+ }
+
public void afterPropertiesSet() throws Exception {
Assert.notNull(flowExecutor, "The FlowExecutor to execute flows is required");
if (flowUrlHandler == null) {
@@ -339,11 +373,11 @@ private void handleFlowExecutionResult(FlowExecutionResult result, ServletExtern
if (context.getFlowDefinitionRedirectRequested()) {
sendFlowDefinitionRedirect(result, context, request, response);
} else if (context.getExternalRedirectRequested()) {
- sendExternalRedirect(context.getExternalRedirectUrl(), request, response);
+ sendExternalRedirect(context.getExternalRedirectUrl(), request, response, result);
} else {
String location = handler.handleExecutionOutcome(result.getOutcome(), request, response);
if (location != null) {
- sendExternalRedirect(location, request, response);
+ sendExternalRedirect(location, request, response, result);
} else {
defaultHandleExecutionOutcome(result.getFlowId(), result.getOutcome(), context, request, response);
}
@@ -382,15 +416,20 @@ private void sendFlowDefinitionRedirect(FlowExecutionResult result, ServletExter
private void sendExternalRedirect(String location, HttpServletRequest request, HttpServletResponse response)
throws IOException {
+ sendExternalRedirect(location, request, response, null);
+ }
+
+ private void sendExternalRedirect(String location, HttpServletRequest request, HttpServletResponse response,
+ FlowExecutionResult result) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Sending external redirect to '" + location + "'");
}
if (location.startsWith(SERVLET_RELATIVE_LOCATION_PREFIX)) {
sendServletRelativeRedirect(location.substring(SERVLET_RELATIVE_LOCATION_PREFIX.length()), request,
- response);
+ response, result);
} else if (location.startsWith(CONTEXT_RELATIVE_LOCATION_PREFIX)) {
sendContextRelativeRedirect(location.substring(CONTEXT_RELATIVE_LOCATION_PREFIX.length()), request,
- response);
+ response, result);
} else if (location.startsWith(SERVER_RELATIVE_LOCATION_PREFIX)) {
String url = location.substring(SERVER_RELATIVE_LOCATION_PREFIX.length());
if (!url.startsWith("/")) {
@@ -401,9 +440,9 @@ private void sendExternalRedirect(String location, HttpServletRequest request, H
sendRedirect(location, request, response);
} else {
if (isRedirectServletRelative(request)) {
- sendServletRelativeRedirect(location, request, response);
+ sendServletRelativeRedirect(location, request, response, result);
} else {
- sendContextRelativeRedirect(location, request, response);
+ sendContextRelativeRedirect(location, request, response, result);
}
}
}
@@ -413,7 +452,7 @@ private void sendExternalRedirect(String location, HttpServletRequest request, H
* such as "contextRelative: was not specified. This answer depends on how the MVC Dispatcher Servlet is mapped: (1)
* default servlet, (2) prefix, (3) extension, (4) exact match. In (1), (3), and (4) it doesn't make sense to
* prepend the servlet path, which contains the entire URL after the context path.
- *
+ *
* Because there is no simple way to get the servlet mapping, this method is implemented to return True if path info
* is not null. Also see SWF-1385.
*/
@@ -421,25 +460,48 @@ private boolean isRedirectServletRelative(HttpServletRequest request) {
return (request.getPathInfo() != null);
}
- private void sendContextRelativeRedirect(String location, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
+ private void sendContextRelativeRedirect(String location, HttpServletRequest request, HttpServletResponse response,
+ FlowExecutionResult result) throws IOException {
StringBuilder url = new StringBuilder(request.getContextPath());
if (!location.startsWith("/")) {
url.append('/');
}
url.append(location);
- sendRedirect(url.toString(), request, response);
+ sendRedirect(url.toString(), request, response, result);
}
- private void sendServletRelativeRedirect(String location, HttpServletRequest request, HttpServletResponse response)
- throws IOException {
+ private void sendServletRelativeRedirect(String location, HttpServletRequest request, HttpServletResponse response,
+ FlowExecutionResult result) throws IOException {
StringBuilder url = new StringBuilder(request.getContextPath());
url.append(request.getServletPath());
if (!location.startsWith("/")) {
url.append('/');
}
url.append(location);
- sendRedirect(url.toString(), request, response);
+ sendRedirect(url.toString(), request, response, result);
+ }
+
+ private void sendRedirect(String url, HttpServletRequest request, HttpServletResponse response,
+ FlowExecutionResult result) throws IOException {
+
+ if (this.saveOutputToFlashScopeOnRedirect) {
+ saveFlashOutput(url.toString(), request, response, result);
+ }
+ sendRedirect(url, request, response);
+ }
+
+ private void saveFlashOutput(String location, HttpServletRequest request, HttpServletResponse response,
+ FlowExecutionResult result) {
+ Map<String, Object> output = result.getOutcome().getOutput().asMap();
+ FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
+ if (flashMapManager != null && output != null && !output.isEmpty()) {
+ UriComponents uriComponents = UriComponentsBuilder.fromUriString(location).build();
+ FlashMap flashMap = new FlashMap();
+ flashMap.setTargetRequestPath(uriComponents.getPath());
+ flashMap.addTargetRequestParams(uriComponents.getQueryParams());
+ flashMap.putAll(output);
+ flashMapManager.saveOutputFlashMap(flashMap, request, response);
+ }
}
private void handleFlowException(FlowException e, HttpServletRequest request, HttpServletResponse response,
222 spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java
View
@@ -13,6 +13,9 @@
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.FlashMap;
+import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.webflow.context.servlet.ServletExternalContext;
import org.springframework.webflow.core.FlowException;
@@ -34,6 +37,7 @@
private LocalAttributeMap<Object> flowInput = new LocalAttributeMap<Object>();
private boolean handleException;
private boolean handleExecutionOutcome;
+ private MockFlashMapManager flashMapManager = new MockFlashMapManager();
protected void setUp() throws Exception {
flowExecutor = EasyMock.createMock(FlowExecutor.class);
@@ -80,14 +84,11 @@ public String handleException(FlowException e, HttpServletRequest request, HttpS
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
context = new ServletExternalContext(servletContext, request, response, flowHandlerAdapter.getFlowUrlHandler());
+ request.setAttribute(DispatcherServlet.FLASH_MAP_MANAGER_ATTRIBUTE, flashMapManager);
}
public void testLaunchFlowRequest() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/whatever");
- request.setRequestURI("/springtravel/app/whatever");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/whatever", "GET");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
EasyMock.expectLastCall().andReturn(result);
@@ -97,11 +98,7 @@ public void testLaunchFlowRequest() throws Exception {
}
public void testLaunchFlowRequestEndsAfterProcessing() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/whatever");
- request.setRequestURI("/springtravel/app/whatever");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/whatever", "GET");
Map<String, String> parameters = new HashMap<String, String>();
request.setParameters(parameters);
flowExecutor.launchExecution("foo", flowInput, context);
@@ -117,11 +114,7 @@ public void testLaunchFlowRequestEndsAfterProcessing() throws Exception {
}
public void testLaunchFlowRequestEndsAfterProcessingAjaxRequest() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/whatever");
- request.setRequestURI("/springtravel/app/whatever");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/whatever", "GET");
Map<String, String> parameters = new HashMap<String, String>();
request.setParameters(parameters);
context.setAjaxRequest(true);
@@ -139,11 +132,7 @@ public void testLaunchFlowRequestEndsAfterProcessingAjaxRequest() throws Excepti
}
public void testResumeFlowRequest() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("POST");
+ setupRequest("/springtravel", "/app", "/foo", "POST");
request.addParameter("execution", "12345");
flowExecutor.resumeExecution("12345", context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "123456");
@@ -154,11 +143,7 @@ public void testResumeFlowRequest() throws Exception {
}
public void testResumeFlowRequestEndsAfterProcessing() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("POST");
+ setupRequest("/springtravel", "/app", "/foo", "POST");
request.addParameter("execution", "12345");
Map<String, String> parameters = new HashMap<String, String>();
request.setParameters(parameters);
@@ -176,11 +161,7 @@ public void testResumeFlowRequestEndsAfterProcessing() throws Exception {
}
public void testResumeFlowRequestEndsAfterProcessingFlowCommittedResponse() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("POST");
+ setupRequest("/springtravel", "/app", "/foo", "POST");
request.addParameter("execution", "12345");
Map<String, String> parameters = new HashMap<String, String>();
request.setParameters(parameters);
@@ -199,11 +180,7 @@ public void testResumeFlowRequestEndsAfterProcessingFlowCommittedResponse() thro
}
public void testLaunchFlowWithExecutionRedirect() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestFlowExecutionRedirect();
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -216,11 +193,7 @@ public void testLaunchFlowWithExecutionRedirect() throws Exception {
}
public void testLaunchFlowWithDefinitionRedirect() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
Map<String, String> parameters = new HashMap<String, String>();
request.setParameters(parameters);
LocalAttributeMap<Object> input = new LocalAttributeMap<Object>();
@@ -241,11 +214,7 @@ public void testLaunchFlowWithDefinitionRedirect() throws Exception {
}
public void testLaunchFlowWithExternalHttpRedirect() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("http://www.paypal.com");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -258,11 +227,7 @@ public void testLaunchFlowWithExternalHttpRedirect() throws Exception {
}
public void testLaunchFlowWithExternalHttpsRedirect() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("https://www.paypal.com");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -275,11 +240,7 @@ public void testLaunchFlowWithExternalHttpsRedirect() throws Exception {
}
public void testLaunchFlowWithExternalRedirectServletRelative() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("servletRelative:bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -292,11 +253,7 @@ public void testLaunchFlowWithExternalRedirectServletRelative() throws Exception
}
public void testLaunchFlowWithExternalRedirectServletRelativeWithSlash() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("servletRelative:/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -309,11 +266,7 @@ public void testLaunchFlowWithExternalRedirectServletRelativeWithSlash() throws
}
public void testLaunchFlowWithExternalRedirectContextRelative() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("contextRelative:bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -326,11 +279,7 @@ public void testLaunchFlowWithExternalRedirectContextRelative() throws Exception
}
public void testLaunchFlowWithExternalRedirectContextRelativeWithSlash() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("contextRelative:/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -343,11 +292,7 @@ public void testLaunchFlowWithExternalRedirectContextRelativeWithSlash() throws
}
public void testLaunchFlowWithExternalRedirectServerRelative() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("serverRelative:bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -360,11 +305,7 @@ public void testLaunchFlowWithExternalRedirectServerRelative() throws Exception
}
public void testLaunchFlowWithExternalRedirectServerRelativeWithSlash() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("serverRelative:/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -378,11 +319,7 @@ public void testLaunchFlowWithExternalRedirectServerRelativeWithSlash() throws E
public void testLaunchFlowWithExternalRedirectNotHttp10Compatible() throws Exception {
flowHandlerAdapter.setRedirectHttp10Compatible(false);
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
context.requestExternalRedirect("serverRelative:/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -396,14 +333,7 @@ public void testLaunchFlowWithExternalRedirectNotHttp10Compatible() throws Excep
}
public void testSwf1385DefaultServletExternalRedirect() throws Exception {
- // The default case in accordance with the servlet spec:
- // "A string containing only the ’/’ character indicates the "default" servlet of the application.
- // In this case the servlet path is the request URI minus the context path and the path info is null."
- request.setContextPath("/springtravel");
- request.setServletPath("/foo");
- request.setPathInfo(null);
- request.setRequestURI("/springtravel/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/foo", null, "GET");
context.requestExternalRedirect("/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -419,11 +349,7 @@ public void testSwf1385DefaultServletExternalRedirectDeviation() throws Exceptio
// Deviation from the default case:
// In some containers the default behavior can be switched so that the contents of the URI after
// the context path is in the path info while the servlet path is empty.
- request.setContextPath("/springtravel");
- request.setServletPath("");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "", "/foo", "GET");
context.requestExternalRedirect("/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -436,10 +362,7 @@ public void testSwf1385DefaultServletExternalRedirectDeviation() throws Exceptio
}
public void testSwf1385DefaultServletExternalRedirectServletRelative() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/foo");
- request.setRequestURI("/springtravel/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/foo", null, "GET");
context.requestExternalRedirect("/bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -452,10 +375,7 @@ public void testSwf1385DefaultServletExternalRedirectServletRelative() throws Ex
}
public void testExternalRedirectServletRelativeWithDefaultServletMapping() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/foo");
- request.setRequestURI("/springtravel/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/foo", null, "GET");
context.requestExternalRedirect("servletRelative:bar");
flowExecutor.launchExecution("foo", flowInput, context);
FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345");
@@ -468,11 +388,7 @@ public void testExternalRedirectServletRelativeWithDefaultServletMapping() throw
}
public void testDefaultHandleFlowException() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
Map<String, String> parameters = new HashMap<String, String>();
request.setParameters(parameters);
flowExecutor.launchExecution("foo", flowInput, context);
@@ -490,11 +406,7 @@ public void testDefaultHandleFlowException() throws Exception {
}
public void testDefaultHandleNoSuchFlowExecutionException() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
request.addParameter("execution", "12345");
flowExecutor.resumeExecution("12345", context);
FlowException flowException = new NoSuchFlowExecutionException(new MockFlowExecutionKey("12345"), null);
@@ -506,11 +418,7 @@ public void testDefaultHandleNoSuchFlowExecutionException() throws Exception {
}
public void testDefaultHandleNoSuchFlowExecutionExceptionAjaxRequest() throws Exception {
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
request.addParameter("execution", "12345");
flowExecutor.resumeExecution("12345", context);
FlowException flowException = new NoSuchFlowExecutionException(new MockFlowExecutionKey("12345"), null);
@@ -524,20 +432,7 @@ public void testDefaultHandleNoSuchFlowExecutionExceptionAjaxRequest() throws Ex
}
public void testHandleFlowOutcomeCustomFlowHandler() throws Exception {
- handleExecutionOutcome = true;
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
- flowExecutor.launchExecution("foo", flowInput, context);
- LocalAttributeMap<Object> output = new LocalAttributeMap<Object>();
- output.put("bar", "baz");
- FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output);
- FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome);
- EasyMock.expectLastCall().andReturn(result);
- EasyMock.replay(new Object[] { flowExecutor });
- flowHandlerAdapter.handle(request, response, flowHandler);
+ doHandleFlowServletRedirectOutcome();
EasyMock.verify(new Object[] { flowExecutor });
}
@@ -545,15 +440,62 @@ public void testHandleFlowExceptionCustomFlowHandler() throws Exception {
handleException = true;
final FlowException flowException = new FlowException("Error") {
};
- request.setContextPath("/springtravel");
- request.setServletPath("/app");
- request.setPathInfo("/foo");
- request.setRequestURI("/springtravel/app/foo");
- request.setMethod("GET");
+ setupRequest("/springtravel", "/app", "/foo", "GET");
flowExecutor.launchExecution("foo", flowInput, context);
EasyMock.expectLastCall().andThrow(flowException);
EasyMock.replay(new Object[] { flowExecutor });
flowHandlerAdapter.handle(request, response, flowHandler);
EasyMock.verify(new Object[] { flowExecutor });
}
+
+ public void testHandleFlowServletRedirectOutcomeWithoutFlash() throws Exception {
+ doHandleFlowServletRedirectOutcome();
+ assertNull(flashMapManager.getFlashMap());
+ }
+
+ public void testHandleFlowServletRedirectOutcomeWithFlash() throws Exception {
+ flowHandlerAdapter.setSaveOutputToFlashScopeOnRedirect(true);
+ doHandleFlowServletRedirectOutcome();
+ assertEquals("baz", flashMapManager.getFlashMap().get("bar"));
+ assertEquals("/springtravel/app/home", flashMapManager.getFlashMap().getTargetRequestPath());
+ }
+
+ private void doHandleFlowServletRedirectOutcome() throws Exception {
+ handleExecutionOutcome = true;
+ setupRequest("/springtravel", "/app", "/foo", "GET");
+ flowExecutor.launchExecution("foo", flowInput, context);
+ LocalAttributeMap<Object> output = new LocalAttributeMap<Object>();
+ output.put("bar", "baz");
+ FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output);
+ FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome);
+ EasyMock.expectLastCall().andReturn(result);
+ EasyMock.replay(new Object[] { flowExecutor });
+ flowHandlerAdapter.handle(request, response, flowHandler);
+ EasyMock.verify(new Object[] { flowExecutor });
+ }
+
+ private void setupRequest(String contextPath, String servletPath, String pathInfo, String method) {
+ request.setContextPath(contextPath);
+ request.setServletPath(servletPath);
+ request.setPathInfo(pathInfo);
+ request.setRequestURI(contextPath + servletPath + (pathInfo == null ? "" : pathInfo));
+ request.setMethod(method);
+ }
+
+ private static class MockFlashMapManager implements FlashMapManager {
+
+ private FlashMap flashMap;
+
+ public FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
+ this.flashMap = flashMap;
+ }
+
+ public FlashMap getFlashMap() {
+ return flashMap;
+ }
+ }
}
27 src/reference/spring-mvc.xml
View
@@ -469,6 +469,31 @@ mvn package
# import into Eclipse</programlisting>
</para>
</sect2>
-
</sect1>
+
+ <sect1 id="spring-mvc-flash-output">
+ <title>Saving Flow Output to MVC Flash Scope</title>
+ <para>Flow output can be automatically saved to MVC flash scope when an <code>end-state</code>
+ performs an internal redirect. This is particularly useful when displaying a summary
+ screen at the end of a flow. For backwards compatibility this feature is disabled by
+ default, to enable set <code>saveOutputToFlashScopeOnRedirect</code> on your
+ <code>FlowHandlerAdapter</code> to <code>true</code>.</para>
+
+ <programlisting language="xml">
+&lt;!-- Enables FlowHandler URL mapping --&gt;
+&lt;bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter"&gt;
+ &lt;property name="flowExecutor" ref="flowExecutor" /&gt;
+ &lt;property name="saveOutputToFlashScopeOnRedirect" value="true" /&gt;
+&lt;/bean&gt;
+ </programlisting>
+
+ <para>The following example will add <code>confirmationNumber</code> to the MVC flash scope
+ before redirecting to the <code>summary</code> screen.</para>
+
+ <programlisting language="xml">
+&lt;end-state id="finish" view="externalRedirect:summary"&gt;
+ &lt;output name="confirmationNumber" value="booking.confirmationNumber" /&gt;
+&lt;/end-state&gt;
+ </programlisting>
+ </sect1>
</chapter>
6 src/reference/whatsnew.xml
View
@@ -23,6 +23,12 @@
included as part of their Spring configuration. See <xref linkend="portlet-jsf"/>.
</para>
</sect2>
+ <sect2 id="whatsnew-swf-mvcflash">
+ <title>Saving Flow Output to Spring MVC Flash Scope</title>
+ <para>
+ Flow output can now be saved to Spring MVC Flash Scope for any <code>end-state</code> that issues an internal redirect. To enable this feature set <code>FlowHandlerAdapter.saveOutputToFlashScopeOnRedirect</code>. See <xref linkend="spring-mvc-flash-output"/>.
+ </para>
+ </sect2>
</sect1>
<sect1 id="whatsnew-swf-230">
<title>Spring Web Flow 2.3</title>
Please sign in to comment.
Something went wrong with that request. Please try again.