diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java index 6f34a95f39..efa6e96041 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java @@ -66,6 +66,8 @@ public final class WebAsyncManager { private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(this.getClass().getSimpleName()); + private Runnable timeoutHandler; + private Object concurrentResult = RESULT_NONE; private Object[] concurrentResultContext; @@ -117,6 +119,15 @@ public final class WebAsyncManager { this.taskExecutor = taskExecutor; } + /** + * Set the handler to use when concurrent handling times out. If not set, by + * default a timeout is handled by returning SERVICE_UNAVAILABLE (503). + * @param timeoutHandler the handler + */ + public void setTimeoutHandler(Runnable timeoutHandler) { + this.timeoutHandler = timeoutHandler; + } + /** * Whether the selected handler for the current request chose to handle the * request asynchronously. A return value of "true" indicates concurrent @@ -348,6 +359,10 @@ public final class WebAsyncManager { Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null"); this.asyncWebRequest.startAsync(); + if (this.timeoutHandler != null) { + this.asyncWebRequest.setTimeoutHandler(this.timeoutHandler); + } + if (logger.isDebugEnabled()) { HttpServletRequest request = asyncWebRequest.getNativeRequest(HttpServletRequest.class); String requestUri = urlPathHelper.getRequestUri(request); diff --git a/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java b/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java index 0645a7df85..fde9f63425 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTests.java @@ -91,11 +91,7 @@ public class WebAsyncManagerTests { @Test public void startCallableProcessing() throws Exception { - Callable task = new Callable() { - public Object call() throws Exception { - return 1; - } - }; + Callable task = new StubCallable(); CallableProcessingInterceptor interceptor = createStrictMock(CallableProcessingInterceptor.class); interceptor.preProcess(this.asyncWebRequest, task); @@ -146,11 +142,7 @@ public class WebAsyncManagerTests { public void startCallableProcessingNullRequest() { WebAsyncManager manager = WebAsyncUtils.getAsyncManager(new MockHttpServletRequest()); try { - manager.startCallableProcessing(new Callable() { - public Object call() throws Exception { - return 1; - } - }); + manager.startCallableProcessing(new StubCallable()); fail("Expected exception"); } catch (IllegalStateException ex) { @@ -191,6 +183,29 @@ public class WebAsyncManagerTests { verify(this.asyncWebRequest, interceptor); } + @Test + public void setTimeoutHandler() throws Exception { + + Runnable timeoutHandler = new Runnable() { public void run() {} }; + this.asyncManager.setTimeoutHandler(timeoutHandler); + + this.asyncWebRequest.startAsync(); + this.asyncWebRequest.setTimeoutHandler(timeoutHandler); + expect(this.asyncWebRequest.isAsyncComplete()).andReturn(false); + this.asyncWebRequest.dispatch(); + replay(this.asyncWebRequest); + + this.asyncManager.startCallableProcessing(new StubCallable()); + + verify(this.asyncWebRequest); + } + + + private final class StubCallable implements Callable { + public Object call() throws Exception { + return 1; + } + } @SuppressWarnings("serial") private static class SyncTaskExecutor extends SimpleAsyncTaskExecutor {