diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java
index 2585bd25..26ff23b0 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java
@@ -27,9 +27,14 @@ import org.springframework.beans.factory.InitializingBean;
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 @@ import org.springframework.webflow.executor.FlowExecutor;
* 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 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
private boolean redirectHttp10Compatible = true;
+ private boolean saveOutputToFlashScopeOnRedirect;
+
/**
* Creates a new flow handler adapter.
* @see #setFlowExecutor(FlowExecutor)
@@ -157,6 +165,32 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
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}.
+ *
+ *
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 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
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 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
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 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
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 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
* 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 @@ public class FlowHandlerAdapter extends WebContentGenerator implements HandlerAd
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 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,
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java
index dba7ad10..a292268e 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java
@@ -13,6 +13,9 @@ import org.springframework.mock.web.MockHttpServletRequest;
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 @@ public class FlowHandlerAdapterTests extends TestCase {
private LocalAttributeMap