Remove unintended dependency on Servlet API in SockJS

Add a factory method in ServerHttpRequest for creating a
ServerHttpAsyncRequestControl.
This commit is contained in:
Rossen Stoyanchev
2013-08-03 11:21:02 -04:00
parent 15a2f03459
commit 123c01908a
7 changed files with 42 additions and 30 deletions

View File

@@ -23,7 +23,7 @@ package org.springframework.http.server;
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public interface ServerHttpAsyncResponseControl { public interface ServerHttpAsyncRequestControl {
/** /**
* Enable asynchronous processing after which the response remains open until a call * Enable asynchronous processing after which the response remains open until a call
@@ -42,7 +42,7 @@ public interface ServerHttpAsyncResponseControl {
/** /**
* Whether asynchronous request processing has been started. * Whether asynchronous request processing has been started.
*/ */
boolean hasStarted(); boolean isStarted();
/** /**
* Causes asynchronous request processing to be completed. * Causes asynchronous request processing to be completed.

View File

@@ -28,6 +28,7 @@ import org.springframework.util.MultiValueMap;
* Represents a server-side HTTP request. * Represents a server-side HTTP request.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.0 * @since 3.0
*/ */
public interface ServerHttpRequest extends HttpRequest, HttpInputMessage { public interface ServerHttpRequest extends HttpRequest, HttpInputMessage {
@@ -39,7 +40,6 @@ public interface ServerHttpRequest extends HttpRequest, HttpInputMessage {
/** /**
* Return the cookie values parsed from the "Cookie" request header. * Return the cookie values parsed from the "Cookie" request header.
* @return the cookies
*/ */
Map<String, Cookie> getCookies(); Map<String, Cookie> getCookies();
@@ -60,4 +60,10 @@ public interface ServerHttpRequest extends HttpRequest, HttpInputMessage {
*/ */
String getRemoteAddress(); String getRemoteAddress();
/**
* Return a control that allows putting the request in asynchronous mode so the
* response remains open until closed explicitly from the current or another thread.
*/
ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response);
} }

View File

@@ -29,12 +29,12 @@ import org.springframework.util.Assert;
/** /**
* A {@link ServerHttpAsyncResponseControl} to use on Servlet containers (Servlet 3.0+). * A {@link ServerHttpAsyncRequestControl} to use on Servlet containers (Servlet 3.0+).
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.0 * @since 4.0
*/ */
public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncResponseControl, AsyncListener { public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncRequestControl, AsyncListener {
private static long NO_TIMEOUT_VALUE = Long.MIN_VALUE; private static long NO_TIMEOUT_VALUE = Long.MIN_VALUE;
@@ -52,27 +52,24 @@ public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncResp
* {@link ServletServerHttpRequest} and {@link ServletServerHttpResponse} * {@link ServletServerHttpRequest} and {@link ServletServerHttpResponse}
* respectively. * respectively.
*/ */
public ServletServerHttpAsyncRequestControl(ServerHttpRequest request, ServerHttpResponse response) { public ServletServerHttpAsyncRequestControl(ServletServerHttpRequest request, ServletServerHttpResponse response) {
Assert.notNull(request, "request is required"); Assert.notNull(request, "request is required");
Assert.notNull(response, "response is required"); Assert.notNull(response, "response is required");
Assert.isInstanceOf(ServletServerHttpRequest.class, request); Assert.isTrue(request.getServletRequest().isAsyncSupported(),
Assert.isInstanceOf(ServletServerHttpResponse.class, response);
this.request = (ServletServerHttpRequest) request;
this.response = (ServletServerHttpResponse) response;
Assert.isTrue(this.request.getServletRequest().isAsyncSupported(),
"Async support must be enabled on a servlet and for all filters involved " + "Async support must be enabled on a servlet and for all filters involved " +
"in async request processing. This is done in Java code using the Servlet API " + "in async request processing. This is done in Java code using the Servlet API " +
"or by adding \"<async-supported>true</async-supported>\" to servlet and " + "or by adding \"<async-supported>true</async-supported>\" to servlet and " +
"filter declarations in web.xml. Also you must use a Servlet 3.0+ container"); "filter declarations in web.xml. Also you must use a Servlet 3.0+ container");
this.request = request;
this.response = response;
} }
@Override @Override
public boolean hasStarted() { public boolean isStarted() {
return ((this.asyncContext != null) && this.request.getServletRequest().isAsyncStarted()); return ((this.asyncContext != null) && this.request.getServletRequest().isAsyncStarted());
} }
@@ -91,7 +88,7 @@ public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncResp
Assert.state(!isCompleted(), "Async processing has already completed"); Assert.state(!isCompleted(), "Async processing has already completed");
if (hasStarted()) { if (isStarted()) {
return; return;
} }
@@ -108,7 +105,7 @@ public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncResp
@Override @Override
public void complete() { public void complete() {
if (hasStarted() && !isCompleted()) { if (isStarted() && !isCompleted()) {
this.asyncContext.complete(); this.asyncContext.complete();
} }
} }

View File

@@ -72,6 +72,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
private MultiValueMap<String, String> queryParams; private MultiValueMap<String, String> queryParams;
private ServerHttpAsyncRequestControl asyncRequestControl;
/** /**
* Construct a new instance of the ServletServerHttpRequest based on the given {@link HttpServletRequest}. * Construct a new instance of the ServletServerHttpRequest based on the given {@link HttpServletRequest}.
@@ -238,4 +239,14 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
return new ByteArrayInputStream(bos.toByteArray()); return new ByteArrayInputStream(bos.toByteArray());
} }
@Override
public ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response) {
if (this.asyncRequestControl == null) {
Assert.isInstanceOf(ServletServerHttpResponse.class, response);
ServletServerHttpResponse servletServerResponse = (ServletServerHttpResponse) response;
this.asyncRequestControl = new ServletServerHttpAsyncRequestControl(this, servletServerResponse);
}
return this.asyncRequestControl;
}
} }

View File

@@ -20,10 +20,9 @@ import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import org.springframework.http.server.ServerHttpAsyncResponseControl; import org.springframework.http.server.ServerHttpAsyncRequestControl;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpAsyncRequestControl;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketHandler;
@@ -48,7 +47,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private ServerHttpResponse response; private ServerHttpResponse response;
private ServerHttpAsyncResponseControl asyncControl; private ServerHttpAsyncRequestControl asyncRequestControl;
private String protocol; private String protocol;
@@ -109,7 +108,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
return; return;
} }
try { try {
this.asyncControl.start(-1); this.asyncRequestControl.start(-1);
scheduleHeartbeat(); scheduleHeartbeat();
tryFlushCache(); tryFlushCache();
} }
@@ -125,14 +124,14 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
Assert.notNull(frameFormat, "expected frameFormat"); Assert.notNull(frameFormat, "expected frameFormat");
this.request = request; this.request = request;
this.response = response; this.response = response;
this.asyncControl = new ServletServerHttpAsyncRequestControl(this.request, this.response); this.asyncRequestControl = request.getAsyncRequestControl(response);
this.frameFormat = frameFormat; this.frameFormat = frameFormat;
} }
@Override @Override
public synchronized boolean isActive() { public synchronized boolean isActive() {
return ((this.asyncControl != null) && (!this.asyncControl.isCompleted())); return ((this.asyncRequestControl != null) && (!this.asyncRequestControl.isCompleted()));
} }
protected BlockingQueue<String> getMessageCache() { protected BlockingQueue<String> getMessageCache() {
@@ -172,10 +171,10 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
protected synchronized void resetRequest() { protected synchronized void resetRequest() {
updateLastActiveTime(); updateLastActiveTime();
if (isActive() && this.asyncControl.hasStarted()) { if (isActive() && this.asyncRequestControl.isStarted()) {
try { try {
logger.debug("Completing asynchronous request"); logger.debug("Completing asynchronous request");
this.asyncControl.complete(); this.asyncRequestControl.complete();
} }
catch (Throwable ex) { catch (Throwable ex) {
logger.error("Failed to complete request: " + ex.getMessage()); logger.error("Failed to complete request: " + ex.getMessage());
@@ -183,7 +182,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
} }
this.request = null; this.request = null;
this.response = null; this.response = null;
this.asyncControl = null; this.asyncRequestControl = null;
} }
@Override @Override

View File

@@ -17,10 +17,9 @@
package org.springframework.web.socket; package org.springframework.web.socket;
import org.junit.Before; import org.junit.Before;
import org.springframework.http.server.ServerHttpAsyncResponseControl; import org.springframework.http.server.ServerHttpAsyncRequestControl;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpAsyncRequestControl;
import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletRequest;
@@ -41,7 +40,7 @@ public class AbstractHttpRequestTests {
protected MockHttpServletResponse servletResponse; protected MockHttpServletResponse servletResponse;
protected ServerHttpAsyncResponseControl asyncControl; protected ServerHttpAsyncRequestControl asyncControl;
@Before @Before
@@ -57,7 +56,7 @@ public class AbstractHttpRequestTests {
protected void resetRequestAndResponse() { protected void resetRequestAndResponse() {
resetRequest(); resetRequest();
resetResponse(); resetResponse();
this.asyncControl = new ServletServerHttpAsyncRequestControl(this.request, this.response); this.asyncControl = this.request.getAsyncRequestControl(this.response);
} }
protected void resetRequest() { protected void resetRequest() {

View File

@@ -246,7 +246,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
@Override @Override
protected void handleTransportRequest(ServerHttpRequest req, ServerHttpResponse res, WebSocketHandler handler, protected void handleTransportRequest(ServerHttpRequest req, ServerHttpResponse res, WebSocketHandler handler,
String sessionId, String transport) throws IOException, SockJsException { String sessionId, String transport) throws SockJsException {
this.sessionId = sessionId; this.sessionId = sessionId;
this.transport = transport; this.transport = transport;