diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java index e2ced10d..6ae3afe5 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/ExternalContext.java @@ -85,7 +85,7 @@ public interface ExternalContext { public SharedAttributeMap getApplicationMap(); /** - * Returns true if the current request is an Ajax request. + * Returns true if the current request is an asynchronous Ajax request. * @return true if the current request is an Ajax request */ public boolean isAjaxRequest(); @@ -123,13 +123,6 @@ public interface ExternalContext { */ public Writer getResponseWriter(); - /** - * Sets an entry in the response header. - * @param name the entry name - * @param value the entry value - */ - public void setResponseHeader(String name, String value); - /** * 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. @@ -156,6 +149,15 @@ public interface ExternalContext { */ public void requestExternalRedirect(String uri); + /** + * Request that the redirect response requested be sent to the client in a manner that causes the client to issue + * the redirect from a popup dialog. Calling this method only has an effect when a redirect has been requested. + * @see #requestFlowExecutionRedirect() + * @see #requestFlowDefinitionRedirect(String, AttributeMap) + * @see #requestExternalRedirect(String) + */ + public void requestRedirectInPopup(); + /** * Has the response been committed? * @return true if yes, false otherwise diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java index 817c1a43..c4cb8c7e 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/servlet/ServletExternalContext.java @@ -22,7 +22,6 @@ import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.springframework.util.StringUtils; import org.springframework.webflow.context.ExternalContext; import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap; @@ -41,12 +40,6 @@ import org.springframework.webflow.core.collection.SharedAttributeMap; */ public class ServletExternalContext implements ExternalContext { - /** The accept header value that signifies an Ajax request */ - private static final String AJAX_ACCEPT_CONTENT_TYPE = "text/html;type=ajax"; - - /** 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 context. */ @@ -109,6 +102,17 @@ public class ServletExternalContext implements ExternalContext { */ private FlowUrlHandler flowUrlHandler; + /** + * Whether this external request context originated from an Ajax request or not. + */ + private boolean ajaxRequest; + + /** + * In the case where a redirect response is requested, this flag indicates if the redirect should be issued from a + * popup dialog. + */ + private boolean redirectInPopup; + /** * Create a new external context wrapping given servlet HTTP request and response and given servlet context. * @param context the servlet context @@ -131,6 +135,15 @@ public class ServletExternalContext implements ExternalContext { init(context, request, response, flowUrlHandler); } + /** + * Indicates if the current request from this client is an ajax request. This flag may effect the handling of + * response writing within Spring Web Flow. + * @param ajaxRequest the ajax request flag + */ + public void setAjaxRequest(boolean ajaxRequest) { + this.ajaxRequest = ajaxRequest; + } + // implementing external context public String getContextPath() { @@ -170,13 +183,7 @@ public class ServletExternalContext implements ExternalContext { } public boolean isAjaxRequest() { - String acceptHeader = request.getHeader("Accept"); - String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM); - if (AJAX_ACCEPT_CONTENT_TYPE.equals(acceptHeader) || StringUtils.hasText(ajaxParam)) { - return true; - } else { - return false; - } + return ajaxRequest; } public String getFlowExecutionUri(String flowId, String flowExecutionKey) { @@ -191,10 +198,6 @@ public class ServletExternalContext implements ExternalContext { } } - public void setResponseHeader(String name, String value) { - response.setHeader(name, value); - } - public boolean isResponseCommitted() { return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested(); } @@ -212,32 +215,63 @@ public class ServletExternalContext implements ExternalContext { flowDefinitionRedirectFlowInput = input; } + public void requestRedirectInPopup() { + redirectInPopup = true; + } + // implementation specific methods + /** + * Returns the flag indicating if a flow execution redirect response has been requested by the flow. + */ public boolean flowExecutionRedirectRequested() { return flowExecutionRedirectRequested; } + /** + * Returns the flag indicating if a flow definition redirect response has been requested by the flow. + */ public boolean flowDefinitionRedirectRequested() { return flowDefinitionRedirectFlowId != null; } + /** + * Returns the id of the flow definition to redirect to. Only set when {@link #flowDefinitionRedirectRequested()} + * returns true. + */ public String getFlowRedirectFlowId() { return flowDefinitionRedirectFlowId; } + /** + * Returns the input to pass the flow definition through the redirect. Only set when + * {@link #flowDefinitionRedirectRequested()} returns true. + */ public AttributeMap getFlowRedirectFlowInput() { return flowDefinitionRedirectFlowInput; } + /** + * Returns the flag indicating if an external redirect response has been requested by the flow. + */ public boolean externalRedirectRequested() { return externalRedirectUrl != null; } + /** + * Returns the URL to redirect to. Only set if {@link #externalRedirectRequested()} returns true. + */ public String getExternalRedirectUrl() { return externalRedirectUrl; } + /** + * If a redirect response has been requested, indicates if the redirect should be issued from a popup dialog. + */ + public boolean redirectInPopup() { + return redirectInPopup; + } + // private helpers private void init(ServletContext context, HttpServletRequest request, HttpServletResponse response, diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java index e1a30757..62a26df4 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java @@ -117,11 +117,4 @@ public interface RequestControlContext extends RequestContext { */ public boolean getAlwaysRedirectOnPause(); - /** - * Request that a redirect be sent to this flow execution after the current request has processed. The current flow - * execution must have its key assigned for this operation to be supported. - * @throws IllegalStateException if the flow execution has not yet had its key assigned - */ - public void sendFlowExecutionRedirect() throws IllegalStateException; - } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java index 48cfb46e..c6bc4e78 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java @@ -38,8 +38,6 @@ import org.springframework.webflow.execution.ViewFactory; */ public class ViewState extends TransitionableState { - private static final String POPUP_VIEW_HEADER = "Flow-Modal-View"; - /** * The list of actions to be executed when this state is entered. */ @@ -163,10 +161,10 @@ public class ViewState extends TransitionableState { protected void doEnter(RequestControlContext context) throws FlowExecutionException { context.assignFlowExecutionKey(); if (shouldRedirect(context)) { - if (context.getExternalContext().isAjaxRequest() && popup) { - context.getExternalContext().setResponseHeader(POPUP_VIEW_HEADER, "true"); + context.getExternalContext().requestFlowExecutionRedirect(); + if (popup) { + context.getExternalContext().requestRedirectInPopup(); } - context.sendFlowExecutionRedirect(); } else { View view = viewFactory.getView(context); render(context, view); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java index 9be2b2c2..6e77bcc6 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.StringUtils; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import org.springframework.webflow.context.servlet.DefaultFlowUrlHandler; @@ -28,6 +29,26 @@ import org.springframework.webflow.executor.FlowExecutor; */ public class FlowController extends AbstractController { + /** + * The response header to be set on an Ajax redirect + */ + private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL"; + + /** + * The response header to be set on an redirect that should be issued from a popup window. + */ + private static final String POPUP_VIEW_HEADER = "Flow-Modal-View"; + + /** + * The accept header value that signifies an Ajax request. + */ + private static final String AJAX_ACCEPT_CONTENT_TYPE = "text/html;type=ajax"; + + /** + * 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"; + private static final Log logger = LogFactory.getLog(FlowController.class); private FlowExecutor flowExecutor; @@ -36,9 +57,6 @@ public class FlowController extends AbstractController { private Map flowHandlers = new HashMap(); - /** The response header to be set on an Ajax redirect */ - private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL"; - /** * @param flowExecutor the web flow executor service */ @@ -89,7 +107,19 @@ public class FlowController extends AbstractController { protected ServletExternalContext createServletExternalContext(HttpServletRequest request, HttpServletResponse response) { - return new ServletExternalContext(getServletContext(), request, response, urlHandler); + ServletExternalContext context = new ServletExternalContext(getServletContext(), request, response, urlHandler); + context.setAjaxRequest(isAjaxRequest(request)); + return context; + } + + protected boolean isAjaxRequest(HttpServletRequest request) { + String acceptHeader = request.getHeader("Accept"); + String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM); + if (AJAX_ACCEPT_CONTENT_TYPE.equals(acceptHeader) || StringUtils.hasText(ajaxParam)) { + return true; + } else { + return false; + } } protected MutableAttributeMap defaultFlowExecutionInputMap(HttpServletRequest request) { @@ -170,7 +200,10 @@ public class FlowController extends AbstractController { private void sendRedirect(ServletExternalContext context, HttpServletResponse response, String targetUrl) throws IOException { if (context.isAjaxRequest()) { - context.setResponseHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl)); + if (context.redirectInPopup()) { + response.setHeader(POPUP_VIEW_HEADER, "true"); + } + response.setHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl)); } else { response.sendRedirect(response.encodeRedirectURL(targetUrl)); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java index 78b63f4a..34f3117e 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java @@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.StringUtils; import org.springframework.web.context.support.WebApplicationObjectSupport; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.ModelAndView; @@ -23,15 +24,32 @@ import org.springframework.webflow.executor.FlowExecutor; public class FlowHandlerAdapter extends WebApplicationObjectSupport implements HandlerAdapter { + /** + * The response header to be set on an Ajax redirect + */ + private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL"; + + /** + * The response header to be set on an redirect that should be issued from a popup window. + */ + private static final String POPUP_VIEW_HEADER = "Flow-Modal-View"; + + /** + * The accept header value that signifies an Ajax request. + */ + private static final String AJAX_ACCEPT_CONTENT_TYPE = "text/html;type=ajax"; + + /** + * 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"; + private static final Log logger = LogFactory.getLog(FlowHandlerAdapter.class); private FlowExecutor flowExecutor; private FlowUrlHandler urlHandler; - /** The response header to be set on an Ajax redirect */ - private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL"; - public FlowHandlerAdapter(FlowExecutor flowExecutor) { this.flowExecutor = flowExecutor; this.urlHandler = new DefaultFlowUrlHandler(); @@ -77,7 +95,19 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H protected ServletExternalContext createServletExternalContext(HttpServletRequest request, HttpServletResponse response) { - return new ServletExternalContext(getServletContext(), request, response, urlHandler); + ServletExternalContext context = new ServletExternalContext(getServletContext(), request, response, urlHandler); + context.setAjaxRequest(isAjaxRequest(request)); + return context; + } + + protected boolean isAjaxRequest(HttpServletRequest request) { + String acceptHeader = request.getHeader("Accept"); + String ajaxParam = request.getParameter(AJAX_SOURCE_PARAM); + if (AJAX_ACCEPT_CONTENT_TYPE.equals(acceptHeader) || StringUtils.hasText(ajaxParam)) { + return true; + } else { + return false; + } } protected MutableAttributeMap defaultFlowExecutionInputMap(HttpServletRequest request) { @@ -159,7 +189,10 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H private void sendRedirect(ServletExternalContext context, HttpServletResponse response, String targetUrl) throws IOException { if (context.isAjaxRequest()) { - context.setResponseHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl)); + if (context.redirectInPopup()) { + response.setHeader(POPUP_VIEW_HEADER, "true"); + } + response.setHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl)); } else { response.sendRedirect(response.encodeRedirectURL(targetUrl)); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockExternalContext.java index c638911d..142aeea4 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockExternalContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockExternalContext.java @@ -18,7 +18,6 @@ package org.springframework.webflow.test; 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; @@ -58,8 +57,6 @@ public class MockExternalContext implements ExternalContext { private boolean ajaxRequest; - private Map responseHeaders = new HashMap(); - private boolean flowExecutionRedirectRequested; private String flowDefinitionRedirectFlowId; @@ -68,6 +65,8 @@ public class MockExternalContext implements ExternalContext { private String externalRedirectUrl; + private boolean redirectInPopup; + /** * Creates a mock external context with an empty request parameter map. Allows for bean style usage. */ @@ -135,10 +134,6 @@ public class MockExternalContext implements ExternalContext { return responseWriter; } - public void setResponseHeader(String name, String value) { - this.responseHeaders.put(name, value); - } - public boolean isResponseCommitted() { return flowExecutionRedirectRequested() || flowDefinitionRedirectRequested() || externalRedirectRequested(); } @@ -147,13 +142,17 @@ public class MockExternalContext implements ExternalContext { flowExecutionRedirectRequested = true; } + public void requestFlowDefinitionRedirect(String flowId, AttributeMap input) { + flowDefinitionRedirectFlowId = flowId; + flowDefinitionRedirectFlowInput = input; + } + public void requestExternalRedirect(String uri) { externalRedirectUrl = uri; } - public void requestFlowDefinitionRedirect(String flowId, AttributeMap input) { - flowDefinitionRedirectFlowId = flowId; - flowDefinitionRedirectFlowInput = input; + public void requestRedirectInPopup() { + redirectInPopup = true; } /** @@ -266,36 +265,54 @@ public class MockExternalContext implements ExternalContext { } /** - * 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 + * Returns the flag indicating if a flow execution redirect response has been requested by the flow. */ - public String getResponseHeader(String name) { - return (String) responseHeaders.get(name); - } - public boolean flowExecutionRedirectRequested() { return flowExecutionRedirectRequested; } + /** + * Returns the flag indicating if a flow definition redirect response has been requested by the flow. + */ public boolean flowDefinitionRedirectRequested() { return flowDefinitionRedirectFlowId != null; } + /** + * Returns the id of the flow definition to redirect to. Only set when {@link #flowDefinitionRedirectRequested()} + * returns true. + */ public String getFlowRedirectFlowId() { return flowDefinitionRedirectFlowId; } + /** + * Returns the input to pass the flow definition through the redirect. Only set when + * {@link #flowDefinitionRedirectRequested()} returns true. + */ public AttributeMap getFlowRedirectFlowInput() { return flowDefinitionRedirectFlowInput; } + /** + * Returns the flag indicating if an external redirect response has been requested by the flow. + */ public boolean externalRedirectRequested() { return externalRedirectUrl != null; } + /** + * Returns the URL to redirect to. Only set if {@link #externalRedirectRequested()} returns true. + */ public String getExternalRedirectUrl() { return externalRedirectUrl; } + /** + * If a redirect response has been requested, indicates if the redirect should be issued from a popup dialog. + */ + public boolean redirectInPopup() { + return redirectInPopup; + } + } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/context/servlet/ServletExternalContextTests.java b/spring-webflow/src/test/java/org/springframework/webflow/context/servlet/ServletExternalContextTests.java index 13f985a5..c74171ed 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/context/servlet/ServletExternalContextTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/context/servlet/ServletExternalContextTests.java @@ -61,12 +61,7 @@ public class ServletExternalContextTests extends TestCase { } public void testAjaxRequestAcceptHeader() { - request.addHeader("Accept", "text/html;type=ajax"); - assertTrue(context.isAjaxRequest()); - } - - public void testAjaxRequestParam() { - request.addParameter("ajaxSource", "myButton"); + context.setAjaxRequest(true); assertTrue(context.isAjaxRequest()); } @@ -94,4 +89,30 @@ public class ServletExternalContextTests extends TestCase { assertEquals("foo", context.getExternalRedirectUrl()); } + public void testCommitExecutionRedirectPopup() { + context.requestFlowExecutionRedirect(); + context.requestRedirectInPopup(); + assertTrue(context.isResponseCommitted()); + assertTrue(context.flowExecutionRedirectRequested()); + assertTrue(context.redirectInPopup()); + } + + public void testCommitFlowRedirectPopup() { + context.requestFlowDefinitionRedirect("foo", null); + context.requestRedirectInPopup(); + assertTrue(context.isResponseCommitted()); + assertTrue(context.flowDefinitionRedirectRequested()); + assertEquals("foo", context.getFlowRedirectFlowId()); + assertTrue(context.redirectInPopup()); + } + + public void testCommitExternalRedirectPopup() { + context.requestExternalRedirect("foo"); + context.requestRedirectInPopup(); + assertTrue(context.isResponseCommitted()); + assertTrue(context.externalRedirectRequested()); + assertEquals("foo", context.getExternalRedirectUrl()); + assertTrue(context.redirectInPopup()); + } + } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java index 46767b96..452c7409 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java @@ -19,6 +19,7 @@ import junit.framework.TestCase; import org.springframework.webflow.engine.support.DefaultTargetStateResolver; import org.springframework.webflow.engine.support.EventIdTransitionCriteria; +import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.test.MockRequestControlContext; /** @@ -27,37 +28,75 @@ import org.springframework.webflow.test.MockRequestControlContext; */ public class ViewStateTests extends TestCase { - public void testEnterViewState() { + public void testEnterViewStateDefaultBehavior() { Flow flow = new Flow("myFlow"); StubViewFactory viewFactory = new StubViewFactory(); ViewState state = new ViewState(flow, "viewState", viewFactory); - state.getTransitionSet().add(new Transition(on("submit"), to("finish"))); - new EndState(flow, "finish"); MockRequestControlContext context = new MockRequestControlContext(flow); state.enter(context); assertTrue("Render not called", context.getFlowScope().contains("renderCalled")); assertFalse(context.getFlowExecutionRedirectSent()); } + public void testEnterViewStateWithVariables() { + Flow flow = new Flow("myFlow"); + StubViewFactory viewFactory = new StubViewFactory(); + ViewState state = new ViewState(flow, "viewState", viewFactory); + state.addVariable(new ViewVariable("foo", new VariableValueFactory() { + public Object createInitialValue(RequestContext context) { + return "bar"; + } + + public void restoreReferences(Object value, RequestContext context) { + + } + + })); + MockRequestControlContext context = new MockRequestControlContext(flow); + state.enter(context); + assertEquals("bar", context.getFlowScope().getString("foo")); + assertTrue("Render not called", context.getFlowScope().contains("renderCalled")); + assertFalse(context.getFlowExecutionRedirectSent()); + } + public void testEnterViewStateWithLocalRedirect() { Flow flow = new Flow("myFlow"); StubViewFactory viewFactory = new StubViewFactory(); ViewState state = new ViewState(flow, "viewState", viewFactory); state.setRedirect(Boolean.TRUE); - state.getTransitionSet().add(new Transition(on("submit"), to("finish"))); - new EndState(flow, "finish"); MockRequestControlContext context = new MockRequestControlContext(flow); state.enter(context); assertFalse("Render called", context.getFlowScope().contains("renderCalled")); assertTrue(context.getMockExternalContext().flowExecutionRedirectRequested()); } + public void testEnterViewStateWithNoLocalRedirect() { + Flow flow = new Flow("myFlow"); + StubViewFactory viewFactory = new StubViewFactory(); + ViewState state = new ViewState(flow, "viewState", viewFactory); + state.setRedirect(Boolean.FALSE); + MockRequestControlContext context = new MockRequestControlContext(flow); + state.enter(context); + assertTrue("Render called", context.getFlowScope().contains("renderCalled")); + assertFalse(context.getMockExternalContext().flowExecutionRedirectRequested()); + } + public void testEnterViewStateWithAlwaysRedirectOnPause() { Flow flow = new Flow("myFlow"); StubViewFactory viewFactory = new StubViewFactory(); ViewState state = new ViewState(flow, "viewState", viewFactory); - state.getTransitionSet().add(new Transition(on("submit"), to("finish"))); - new EndState(flow, "finish"); + MockRequestControlContext context = new MockRequestControlContext(flow); + context.setAlwaysRedirectOnPause(true); + state.enter(context); + assertFalse("Render called", context.getFlowScope().contains("renderCalled")); + assertTrue(context.getMockExternalContext().flowExecutionRedirectRequested()); + } + + public void testEnterViewStateWithPopup() { + Flow flow = new Flow("myFlow"); + StubViewFactory viewFactory = new StubViewFactory(); + ViewState state = new ViewState(flow, "viewState", viewFactory); + state.setPopup(true); MockRequestControlContext context = new MockRequestControlContext(flow); context.setAlwaysRedirectOnPause(true); state.enter(context); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java index 2c01da55..00e210bd 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java @@ -142,11 +142,55 @@ public class FlowControllerTests extends TestCase { EasyMock.replay(new Object[] { executor }); ModelAndView mv = controller.handleRequest(request, response); assertNull(mv); - EasyMock.verify(new Object[] { executor }); assertEquals("/springtravel/app/foo?execution=12345", response.getRedirectedUrl()); EasyMock.verify(new Object[] { executor }); } + public void testLaunchFlowWithExecutionRedirectAjaxHeaderOpenInPopup() throws Exception { + request.setContextPath("/springtravel"); + request.setServletPath("/app"); + request.setPathInfo("/foo"); + request.setRequestURI("/springtravel/app/foo"); + request.setMethod("GET"); + request.addHeader("Accept", "text/html;type=ajax"); + Map parameters = new HashMap(); + request.setParameters(parameters); + context.setAjaxRequest(true); + context.requestFlowExecutionRedirect(); + context.requestRedirectInPopup(); + executor.launchExecution("foo", new LocalAttributeMap(parameters), context); + FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345"); + EasyMock.expectLastCall().andReturn(result); + EasyMock.replay(new Object[] { executor }); + ModelAndView mv = controller.handleRequest(request, response); + assertNull(mv); + assertEquals(null, response.getRedirectedUrl()); + assertEquals("true", response.getHeader("Flow-Modal-View")); + assertEquals("/springtravel/app/foo?execution=12345", response.getHeader("Flow-Redirect-URL")); + EasyMock.verify(new Object[] { executor }); + } + + public void testLaunchFlowWithExecutionRedirectAjaxParameter() throws Exception { + request.setContextPath("/springtravel"); + request.setServletPath("/app"); + request.setPathInfo("/foo"); + request.setRequestURI("/springtravel/app/foo"); + request.setMethod("GET"); + request.addParameter("ajaxSource", "this"); + context.setAjaxRequest(true); + context.requestFlowExecutionRedirect(); + executor.launchExecution("foo", new LocalAttributeMap(request.getParameterMap()), context); + FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345"); + EasyMock.expectLastCall().andReturn(result); + EasyMock.replay(new Object[] { executor }); + ModelAndView mv = controller.handleRequest(request, response); + assertNull(mv); + assertEquals(null, response.getRedirectedUrl()); + assertEquals(null, response.getHeader("Flow-Modal-View")); + assertEquals("/springtravel/app/foo?execution=12345", response.getHeader("Flow-Redirect-URL")); + EasyMock.verify(new Object[] { executor }); + } + public void testLaunchFlowWithDefinitionRedirect() throws Exception { request.setContextPath("/springtravel"); request.setServletPath("/app");