response complete status tracking

This commit is contained in:
Keith Donald
2009-04-06 19:55:09 +00:00
parent 3137ad2fcd
commit a1ed5ac6d9
6 changed files with 306 additions and 76 deletions

View File

@@ -134,11 +134,14 @@ public interface ExternalContext {
/**
* Get a writer for writing out a response.
* @return the writer
* @throws IllegalStateException if the response has completed or is not allowed
*/
public Writer getResponseWriter();
public Writer getResponseWriter() throws IllegalStateException;
/**
* Is a response allowed to be written for this request?
* Is a <i>render</i> response allowed to be written for this request? Always return false after a response has been
* completed. May return false before that to indicate a response is not allowed to be completed. For example, in a
* Portlet environment, render responses are only allowed in render requests.
* @return true if yes, false otherwise
*/
public boolean isResponseAllowed();
@@ -148,8 +151,9 @@ public interface ExternalContext {
* flow execution to request a refresh operation, usually to support "refresh after event processing" behavior.
* Calling this method also sets responseComplete status to true.
* @see #isResponseComplete()
* @throws IllegalStateException if the response has completed or is not allowed
*/
public void requestFlowExecutionRedirect();
public void requestFlowExecutionRedirect() throws IllegalStateException;
/**
* Request that a flow definition redirect be performed by the calling environment. Typically called from within a
@@ -158,25 +162,28 @@ public interface ExternalContext {
* @see #isResponseComplete()
* @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
* @throws IllegalStateException if the response has completed or is not allowed
*/
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input);
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) throws IllegalStateException;
/**
* Request a redirect to an arbitrary resource location. May not be supported in some environments. Calling this
* method also sets responseComplete status to true.
* @see #isResponseComplete()
* @param location the location of the resource to redirect to
* @throws IllegalStateException if the response has completed or is not allowed
*/
public void requestExternalRedirect(String location);
public void requestExternalRedirect(String location) throws IllegalStateException;
/**
* 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.
* Request that the current redirect requested be sent to the client in a manner that causes the client to issue the
* redirect from a popup dialog. Only call this method after a redirect has been requested.
* @see #requestFlowExecutionRedirect()
* @see #requestFlowDefinitionRedirect(String, MutableAttributeMap)
* @see #requestExternalRedirect(String)
* @throws IllegalStateException if a redirect has not been requested
*/
public void requestRedirectInPopup();
public void requestRedirectInPopup() throws IllegalStateException;
/**
* Called by flow artifacts such as View states and end states to indicate they handled the response, typically by

View File

@@ -202,17 +202,15 @@ public class PortletExternalContext implements ExternalContext {
}
public String getFlowExecutionUrl(String flowId, String flowExecutionKey) {
if (isRenderPhase()) {
return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, (RenderResponse) response);
} else {
throw new IllegalStateException("You can only obtain a flow execution URL in a RenderRequest");
if (!isRenderPhase()) {
throw new IllegalStateException(
"A flow execution action URL can only be obtained in a RenderRequest using a RenderResponse");
}
return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, (RenderResponse) response);
}
public Writer getResponseWriter() {
if (!isRenderPhase()) {
throw new IllegalStateException("You can only access a response Writer in a RenderRequest");
}
public Writer getResponseWriter() throws IllegalStateException {
assertResponseAllowed();
try {
return ((RenderResponse) response).getWriter();
} catch (IOException e) {
@@ -223,7 +221,7 @@ public class PortletExternalContext implements ExternalContext {
}
public boolean isResponseAllowed() {
return isRenderPhase();
return isRenderPhase() && !responseComplete;
}
public boolean isResponseComplete() {
@@ -234,39 +232,32 @@ public class PortletExternalContext implements ExternalContext {
responseComplete = true;
}
public void requestFlowExecutionRedirect() {
if (isRenderPhase()) {
throw new IllegalStateException("Redirects are not allowed durring the portlet render phase");
}
assertResponseNotAlreadyCompleted();
public void requestFlowExecutionRedirect() throws IllegalStateException {
assertRedirectResponseAllowed();
flowExecutionRedirectRequested = true;
recordResponseComplete();
}
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) {
if (isRenderPhase()) {
throw new IllegalStateException("Redirects are not allowed durring the portlet render phase");
}
assertResponseNotAlreadyCompleted();
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) throws IllegalStateException {
assertRedirectResponseAllowed();
flowDefinitionRedirectFlowId = flowId;
flowDefinitionRedirectFlowInput = input;
recordResponseComplete();
}
public void requestExternalRedirect(String uri) {
if (isRenderPhase()) {
throw new IllegalStateException("Redirects are not allowed durring the portlet render phase");
}
assertResponseNotAlreadyCompleted();
public void requestExternalRedirect(String uri) throws IllegalStateException {
assertRedirectResponseAllowed();
externalRedirectUrl = uri;
recordResponseComplete();
}
public void requestRedirectInPopup() {
if (isRenderPhase()) {
throw new IllegalStateException("Redirects are not allowed durring the portlet render phase");
public void requestRedirectInPopup() throws IllegalStateException {
if (isRedirectRequested()) {
redirectInPopup = true;
} else {
throw new IllegalStateException(
"Only call requestRedirectInPopup after a redirect has been requested by calling requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect");
}
redirectInPopup = true;
}
public boolean isRedirectRequested() {
@@ -362,10 +353,25 @@ public class PortletExternalContext implements ExternalContext {
}
}
private void assertResponseNotAlreadyCompleted() {
private void assertResponseAllowed() throws IllegalStateException {
if (!isRenderPhase()) {
throw new IllegalStateException(
"A response is not allowed because the current PortletRequest is not a RenderRequest");
}
if (responseComplete) {
throw new IllegalStateException(
"The ExternalContext response has already been completed; this would have been done with a previous call to recordResponseComplete, requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect");
"A response is not allowed because recordResponseComplete() has already been called on this ExternalContext");
}
}
private void assertRedirectResponseAllowed() throws IllegalStateException {
if (!isActionPhase()) {
throw new IllegalStateException(
"A redirect is not allowed because the current PortletRequest is not a ActionRequest");
}
if (responseComplete) {
throw new IllegalStateException(
"A redirect is not allowed because a response has already been completed on this ExternalContext");
}
}

View File

@@ -205,7 +205,8 @@ public class ServletExternalContext implements ExternalContext {
return flowUrlHandler.createFlowExecutionUrl(flowId, flowExecutionKey, request);
}
public Writer getResponseWriter() {
public Writer getResponseWriter() throws IllegalStateException {
assertResponseAllowed();
try {
return response.getWriter();
} catch (IOException e) {
@@ -216,7 +217,7 @@ public class ServletExternalContext implements ExternalContext {
}
public boolean isResponseAllowed() {
return true;
return !responseComplete;
}
public boolean isResponseComplete() {
@@ -227,27 +228,32 @@ public class ServletExternalContext implements ExternalContext {
responseComplete = true;
}
public void requestFlowExecutionRedirect() {
assertResponseNotAlreadyCompleted();
public void requestFlowExecutionRedirect() throws IllegalStateException {
assertResponseAllowed();
flowExecutionRedirectRequested = true;
recordResponseComplete();
}
public void requestExternalRedirect(String location) {
assertResponseNotAlreadyCompleted();
externalRedirectUrl = location;
recordResponseComplete();
}
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) {
assertResponseNotAlreadyCompleted();
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) throws IllegalStateException {
assertResponseAllowed();
flowDefinitionRedirectFlowId = flowId;
flowDefinitionRedirectFlowInput = input;
recordResponseComplete();
}
public void requestRedirectInPopup() {
redirectInPopup = true;
public void requestExternalRedirect(String location) throws IllegalStateException {
assertResponseAllowed();
externalRedirectUrl = location;
recordResponseComplete();
}
public void requestRedirectInPopup() throws IllegalStateException {
if (isRedirectRequested()) {
redirectInPopup = true;
} else {
throw new IllegalStateException(
"Only call requestRedirectInPopup after a redirect has been requested by calling requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect");
}
}
public boolean isRedirectRequested() {
@@ -352,10 +358,22 @@ public class ServletExternalContext implements ExternalContext {
this.flowUrlHandler = flowUrlHandler;
}
private void assertResponseNotAlreadyCompleted() {
if (responseComplete) {
private void assertResponseAllowed() throws IllegalStateException {
if (!isResponseAllowed()) {
if (getFlowExecutionRedirectRequested()) {
throw new IllegalStateException(
"A response is not allowed because a redirect has already been requested on this ExternalContext");
}
if (getFlowDefinitionRedirectRequested()) {
throw new IllegalStateException(
"A response is not allowed because a flowRedirect has already been requested on this ExternalContext");
}
if (getExternalRedirectRequested()) {
throw new IllegalStateException(
"A response is not allowed because an externalRedirect has already been requested on this ExternalContext");
}
throw new IllegalStateException(
"The ExternalContext response has already been completed; this would have been done with a previous call to recordResponseComplete, requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect");
"A response is not allowed because one has already been completed on this ExternalContext");
}
}

View File

@@ -147,42 +147,48 @@ public class MockExternalContext implements ExternalContext {
}
public Writer getResponseWriter() {
assertResponseAllowed();
return responseWriter;
}
public boolean isResponseAllowed() {
return true;
return !responseComplete;
}
public boolean isResponseComplete() {
return responseComplete;
}
public void recordResponseComplete() throws IllegalStateException {
public void recordResponseComplete() {
responseComplete = true;
}
public void requestFlowExecutionRedirect() {
assertResponseNotAlreadyCompleted();
public void requestFlowExecutionRedirect() throws IllegalStateException {
assertResponseAllowed();
flowExecutionRedirectRequested = true;
recordResponseComplete();
}
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) {
assertResponseNotAlreadyCompleted();
public void requestFlowDefinitionRedirect(String flowId, MutableAttributeMap input) throws IllegalStateException {
assertResponseAllowed();
flowDefinitionRedirectFlowId = flowId;
flowDefinitionRedirectFlowInput = input;
recordResponseComplete();
}
public void requestExternalRedirect(String uri) {
assertResponseNotAlreadyCompleted();
public void requestExternalRedirect(String uri) throws IllegalStateException {
assertResponseAllowed();
externalRedirectUrl = uri;
recordResponseComplete();
}
public void requestRedirectInPopup() {
redirectInPopup = true;
public void requestRedirectInPopup() throws IllegalStateException {
if (isRedirectRequested()) {
redirectInPopup = true;
} else {
throw new IllegalStateException(
"Only call requestRedirectInPopup after a redirect has been requested by calling requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect");
}
}
public boolean isRedirectRequested() {
@@ -400,10 +406,22 @@ public class MockExternalContext implements ExternalContext {
return redirectInPopup;
}
private void assertResponseNotAlreadyCompleted() {
if (responseComplete) {
private void assertResponseAllowed() throws IllegalStateException {
if (!isResponseAllowed()) {
if (getFlowExecutionRedirectRequested()) {
throw new IllegalStateException(
"A response is not allowed because a redirect has already been requested on this ExternalContext");
}
if (getFlowDefinitionRedirectRequested()) {
throw new IllegalStateException(
"A response is not allowed because a flowRedirect has already been requested on this ExternalContext");
}
if (getExternalRedirectRequested()) {
throw new IllegalStateException(
"A response is not allowed because an externalRedirect has already been requested on this ExternalContext");
}
throw new IllegalStateException(
"The ExternalContext response has already been completed; this would have been done with a previous call to recordResponseComplete, requestFlowExecutionRedirect, requestFlowDefinitionRedirect, or requestExternalRedirect");
"A response is not allowed because one has already been completed on this ExternalContext");
}
}

View File

@@ -15,13 +15,14 @@
*/
package org.springframework.webflow.context.portlet;
import java.io.IOException;
import java.io.Writer;
import junit.framework.TestCase;
import org.springframework.mock.web.portlet.MockActionRequest;
import org.springframework.mock.web.portlet.MockActionResponse;
import org.springframework.mock.web.portlet.MockPortletContext;
import org.springframework.mock.web.portlet.MockPortletRequest;
import org.springframework.mock.web.portlet.MockPortletResponse;
import org.springframework.mock.web.portlet.MockRenderRequest;
import org.springframework.mock.web.portlet.MockRenderResponse;
import org.springframework.webflow.context.servlet.ServletExternalContext;
@@ -33,15 +34,15 @@ public class PortletExternalContextTests extends TestCase {
private MockPortletContext portletContext;
private MockPortletRequest request;
private MockActionRequest request;
private MockPortletResponse response;
private MockActionResponse response;
private PortletExternalContext context;
private MockPortletRequest renderRequest;
private MockRenderRequest renderRequest;
private MockPortletResponse renderResponse;
private MockRenderResponse renderResponse;
private PortletExternalContext renderContext;
@@ -83,8 +84,11 @@ public class PortletExternalContextTests extends TestCase {
}
public void testCommitExecutionRedirect() {
assertFalse(context.isResponseAllowed());
context.requestFlowExecutionRedirect();
assertTrue(context.getFlowExecutionRedirectRequested());
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testCommitExecutionRedirectRenderRequest() {
@@ -97,9 +101,12 @@ public class PortletExternalContextTests extends TestCase {
}
public void testCommitFlowRedirect() {
assertFalse(context.isResponseAllowed());
context.requestFlowDefinitionRedirect("foo", null);
assertTrue(context.getFlowDefinitionRedirectRequested());
assertEquals("foo", context.getFlowRedirectFlowId());
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testCommitFlowRedirectRenderRequest() {
@@ -112,9 +119,11 @@ public class PortletExternalContextTests extends TestCase {
}
public void testCommitExternalRedirect() {
assertFalse(context.isResponseAllowed());
context.requestExternalRedirect("foo");
assertTrue(context.getExternalRedirectRequested());
assertEquals("foo", context.getExternalRedirectUrl());
assertTrue(context.isResponseComplete());
}
public void testCommitExternalRedirectRenderRequest() {
@@ -127,10 +136,12 @@ public class PortletExternalContextTests extends TestCase {
}
public void testCommitExecutionRedirectPopup() {
assertFalse(context.isResponseAllowed());
context.requestFlowExecutionRedirect();
context.requestRedirectInPopup();
assertTrue(context.getFlowExecutionRedirectRequested());
assertTrue(context.getRedirectInPopup());
assertTrue(context.isResponseComplete());
}
public void testCommitFlowRedirectPopup() {
@@ -139,6 +150,7 @@ public class PortletExternalContextTests extends TestCase {
assertTrue(context.getFlowDefinitionRedirectRequested());
assertEquals("foo", context.getFlowRedirectFlowId());
assertTrue(context.getRedirectInPopup());
assertTrue(context.isResponseComplete());
}
public void testCommitExternalRedirectPopup() {
@@ -147,6 +159,7 @@ public class PortletExternalContextTests extends TestCase {
assertTrue(context.getExternalRedirectRequested());
assertEquals("foo", context.getExternalRedirectUrl());
assertTrue(context.getRedirectInPopup());
assertTrue(context.isResponseComplete());
}
public void testExecutionRedirectPopupRenderRequest() {
@@ -170,4 +183,64 @@ public class PortletExternalContextTests extends TestCase {
assertFalse(context.isRenderPhase());
}
public void testRecordResponseComplete() {
context.recordResponseComplete();
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testDoubleCommitResponse() {
context.recordResponseComplete();
try {
context.requestExternalRedirect("foo");
} catch (IllegalStateException e) {
}
try {
context.requestFlowExecutionRedirect();
fail("Should have failed");
} catch (IllegalStateException e) {
}
try {
context.requestFlowDefinitionRedirect("foo", null);
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testRedirectInPopup() {
assertFalse(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
context.requestFlowExecutionRedirect();
assertTrue(context.isResponseComplete());
context.requestRedirectInPopup();
assertTrue(context.getRedirectInPopup());
assertFalse(context.isResponseAllowed());
assertTrue(context.getRedirectInPopup());
}
public void testRedirectInPopupNoRedirectRequested() {
try {
context.requestRedirectInPopup();
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testGetResponseWriter() throws IOException {
Writer writer = renderContext.getResponseWriter();
writer.append('t');
assertEquals("t", renderResponse.getContentAsString());
}
public void testGetResponseWriterResponseComplete() throws IOException {
context.recordResponseComplete();
try {
context.getResponseWriter();
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.webflow.context.servlet;
import java.io.IOException;
import java.io.Writer;
import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
@@ -36,7 +39,10 @@ public class ServletExternalContextTests extends TestCase {
protected void setUp() {
servletContext = new MockServletContext();
servletContext.setAttribute("aFoo", "bar");
request = new MockHttpServletRequest();
request.setAttribute("rFoo", "bar");
request.getSession(true).setAttribute("sFoo", "bar");
response = new MockHttpServletResponse();
context = new ServletExternalContext(servletContext, request, response);
}
@@ -50,6 +56,18 @@ public class ServletExternalContextTests extends TestCase {
assertTrue(context.getRequestParameterMap().isEmpty());
}
public void testGetAppAttribute() {
assertEquals("bar", context.getApplicationMap().get("aFoo"));
}
public void testGetSessionAttribute() {
assertEquals("bar", context.getSessionMap().get("sFoo"));
}
public void testGetRequestAttribute() {
assertEquals("bar", context.getRequestMap().get("rFoo"));
}
public void testGetNativeObjects() {
assertEquals(servletContext, context.getNativeContext());
assertEquals(request, context.getNativeRequest());
@@ -72,18 +90,22 @@ public class ServletExternalContextTests extends TestCase {
public void testCommitExecutionRedirect() {
context.requestFlowExecutionRedirect();
assertTrue(context.getFlowExecutionRedirectRequested());
assertTrue(context.isResponseComplete());
}
public void testCommitFlowRedirect() {
context.requestFlowDefinitionRedirect("foo", null);
assertTrue(context.getFlowDefinitionRedirectRequested());
assertEquals("foo", context.getFlowRedirectFlowId());
assertTrue(context.isResponseComplete());
}
public void testCommitExternalRedirect() {
context.requestExternalRedirect("foo");
assertTrue(context.getExternalRedirectRequested());
assertEquals("foo", context.getExternalRedirectUrl());
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testCommitExecutionRedirectPopup() {
@@ -91,6 +113,8 @@ public class ServletExternalContextTests extends TestCase {
context.requestRedirectInPopup();
assertTrue(context.getFlowExecutionRedirectRequested());
assertTrue(context.getRedirectInPopup());
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testCommitFlowRedirectPopup() {
@@ -99,6 +123,8 @@ public class ServletExternalContextTests extends TestCase {
assertTrue(context.getFlowDefinitionRedirectRequested());
assertEquals("foo", context.getFlowRedirectFlowId());
assertTrue(context.getRedirectInPopup());
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testCommitExternalRedirectPopup() {
@@ -107,10 +133,92 @@ public class ServletExternalContextTests extends TestCase {
assertTrue(context.getExternalRedirectRequested());
assertEquals("foo", context.getExternalRedirectUrl());
assertTrue(context.getRedirectInPopup());
assertFalse(context.isResponseAllowed());
}
public void testResponseAllowed() {
assertTrue(context.isResponseAllowed());
public void testRecordResponseComplete() {
context.recordResponseComplete();
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
}
public void testDoubleCommitResponse() {
context.recordResponseComplete();
try {
context.requestFlowExecutionRedirect();
fail("Should have failed");
} catch (IllegalStateException e) {
}
try {
context.requestFlowDefinitionRedirect("foo", null);
fail("Should have failed");
} catch (IllegalStateException e) {
}
try {
context.requestExternalRedirect("foo");
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testDoubleCommitResponseExecutionRedirectFirst() {
context.requestFlowExecutionRedirect();
try {
context.requestFlowDefinitionRedirect("foo", null);
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testDoubleCommitResponseDefinitionRedirectFirst() {
context.requestFlowDefinitionRedirect("foo", null);
try {
context.requestFlowDefinitionRedirect("foo", null);
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testDoubleCommitResponseExternalRedirectFirst() {
context.requestExternalRedirect("foo");
try {
context.requestFlowDefinitionRedirect("foo", null);
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testRedirectInPopup() {
context.requestFlowExecutionRedirect();
assertTrue(context.isResponseComplete());
assertFalse(context.isResponseAllowed());
context.requestRedirectInPopup();
assertTrue(context.getRedirectInPopup());
}
public void testRedirectInPopupNoRedirectRequested() {
try {
context.requestRedirectInPopup();
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
public void testGetResponseWriter() throws IOException {
Writer writer = context.getResponseWriter();
writer.append('t');
assertEquals("t", response.getContentAsString());
}
public void testGetResponseWriterResponseComplete() throws IOException {
context.recordResponseComplete();
try {
context.getResponseWriter();
fail("Should have failed");
} catch (IllegalStateException e) {
}
}
}