Add onTimeout/onCompletion callbacks to DeferredResult
Issue: SPR-9914
This commit is contained in:
parent
3a09644843
commit
d701464517
|
|
@ -178,7 +178,7 @@ public class OpenSessionInViewTests {
|
|||
|
||||
AsyncWebRequest asyncWebRequest = createStrictMock(AsyncWebRequest.class);
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.setTimeoutHandler((Runnable) anyObject());
|
||||
asyncWebRequest.addTimeoutHandler((Runnable) anyObject());
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.startAsync();
|
||||
replay(asyncWebRequest);
|
||||
|
|
@ -494,7 +494,7 @@ public class OpenSessionInViewTests {
|
|||
|
||||
AsyncWebRequest asyncWebRequest = createMock(AsyncWebRequest.class);
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.setTimeoutHandler(EasyMock.<Runnable>anyObject());
|
||||
asyncWebRequest.addTimeoutHandler(EasyMock.<Runnable>anyObject());
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.startAsync();
|
||||
expect(asyncWebRequest.isAsyncStarted()).andReturn(true).anyTimes();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import static org.easymock.EasyMock.anyObject;
|
|||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.createStrictMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reset;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
|
|
@ -49,8 +48,8 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
|
|||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.async.AsyncWebRequest;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.context.request.async.WebAsyncManager;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
|
||||
/**
|
||||
|
|
@ -155,7 +154,7 @@ public class OpenEntityManagerInViewTests extends TestCase {
|
|||
|
||||
AsyncWebRequest asyncWebRequest = createStrictMock(AsyncWebRequest.class);
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.setTimeoutHandler((Runnable) anyObject());
|
||||
asyncWebRequest.addTimeoutHandler((Runnable) anyObject());
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.startAsync();
|
||||
replay(asyncWebRequest);
|
||||
|
|
@ -346,7 +345,7 @@ public class OpenEntityManagerInViewTests extends TestCase {
|
|||
|
||||
AsyncWebRequest asyncWebRequest = createMock(AsyncWebRequest.class);
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.setTimeoutHandler((Runnable) anyObject());
|
||||
asyncWebRequest.addTimeoutHandler((Runnable) anyObject());
|
||||
asyncWebRequest.addCompletionHandler((Runnable) anyObject());
|
||||
asyncWebRequest.startAsync();
|
||||
expect(asyncWebRequest.isAsyncStarted()).andReturn(true).anyTimes();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
@SuppressWarnings("serial")
|
||||
final class TestDispatcherServlet extends DispatcherServlet {
|
||||
|
||||
private static final String KEY = TestDispatcherServlet.class.getName() + "-interceptor";
|
||||
private static final String KEY = TestDispatcherServlet.class.getName() + ".interceptor";
|
||||
|
||||
/**
|
||||
* Create a new instance with the given web application context.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import org.hamcrest.Matchers;
|
|||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
import org.springframework.web.context.request.async.AsyncTask;
|
||||
import org.springframework.web.context.request.async.MvcAsyncTask;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
|
||||
/**
|
||||
|
|
@ -97,7 +97,7 @@ public class RequestResultMatchers {
|
|||
/**
|
||||
* Assert the result from asynchronous processing.
|
||||
* This method can be used when a controller method returns {@link Callable}
|
||||
* or {@link AsyncTask}. The value matched is the value returned from the
|
||||
* or {@link MvcAsyncTask}. The value matched is the value returned from the
|
||||
* {@code Callable} or the exception raised.
|
||||
*/
|
||||
public <T> ResultMatcher asyncResult(Object expectedResult) {
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ public interface AsyncWebRequest extends NativeWebRequest {
|
|||
void setTimeout(Long timeout);
|
||||
|
||||
/**
|
||||
* Set the handler to use when concurrent handling has timed out.
|
||||
* Add a handler to invoke when concurrent handling has timed out.
|
||||
*/
|
||||
void setTimeoutHandler(Runnable runnable);
|
||||
void addTimeoutHandler(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Add a Runnable to be invoked when request processing completes.
|
||||
* Add a handle to invoke when request processing completes.
|
||||
*/
|
||||
void addCompletionHandler(Runnable runnable);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ class CallableInterceptorChain {
|
|||
private int preProcessIndex = -1;
|
||||
|
||||
|
||||
public CallableInterceptorChain(Collection<CallableProcessingInterceptor> interceptors) {
|
||||
this.interceptors = new ArrayList<CallableProcessingInterceptor>(interceptors);
|
||||
public CallableInterceptorChain(List<CallableProcessingInterceptor> interceptors) {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
public void applyPreProcess(NativeWebRequest request, Callable<?> task) throws Exception {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.util.concurrent.Callable;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* {@code DeferredResult} provides an alternative to using a {@link Callable}
|
||||
|
|
@ -41,6 +42,10 @@ public final class DeferredResult<T> {
|
|||
|
||||
private final Object timeoutResult;
|
||||
|
||||
private Runnable timeoutCallback;
|
||||
|
||||
private Runnable completionCallback;
|
||||
|
||||
private DeferredResultHandler resultHandler;
|
||||
|
||||
private Object result = RESULT_NONE;
|
||||
|
|
@ -56,7 +61,7 @@ public final class DeferredResult<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a DeferredResult with a timeout.
|
||||
* Create a DeferredResult with a timeout value.
|
||||
* @param timeout timeout value in milliseconds
|
||||
*/
|
||||
public DeferredResult(long timeout) {
|
||||
|
|
@ -64,7 +69,8 @@ public final class DeferredResult<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a DeferredResult with a timeout and a default result to use on timeout.
|
||||
* Create a DeferredResult with a timeout value and a default result to use
|
||||
* in case of timeout.
|
||||
* @param timeout timeout value in milliseconds; ignored if {@code null}
|
||||
* @param timeoutResult the result to use
|
||||
*/
|
||||
|
|
@ -73,13 +79,48 @@ public final class DeferredResult<T> {
|
|||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if this DeferredResult is no longer usable either
|
||||
* because it was previously set or because the underlying request expired.
|
||||
* <p>
|
||||
* The result may have been set with a call to {@link #setResult(Object)},
|
||||
* or {@link #setErrorResult(Object)}, or as a result of a timeout, if a
|
||||
* timeout result was provided to the constructor. The request may also
|
||||
* expire due to a timeout or network error.
|
||||
*/
|
||||
public boolean isSetOrExpired() {
|
||||
return ((this.result != RESULT_NONE) || this.expired);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured timeout value in milliseconds.
|
||||
*/
|
||||
public Long getTimeoutMilliseconds() {
|
||||
Long getTimeoutValue() {
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register code to invoke when the async request times out. This method is
|
||||
* called from a container thread when an async request times out before the
|
||||
* {@code DeferredResult} has been set. It may invoke
|
||||
* {@link DeferredResult#setResult(Object) setResult} or
|
||||
* {@link DeferredResult#setErrorResult(Object) setErrorResult} to resume
|
||||
* processing.
|
||||
*/
|
||||
public void onTimeout(Runnable callback) {
|
||||
this.timeoutCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register code to invoke when the async request completes. This method is
|
||||
* called from a container thread when an async request completed for any
|
||||
* reason including timeout and network error. This method is useful for
|
||||
* detecting that a {@code DeferredResult} instance is no longer usable.
|
||||
*/
|
||||
public void onCompletion(Runnable callback) {
|
||||
this.completionCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a handler to use to handle the result value.
|
||||
* @param resultHandler the handler
|
||||
|
|
@ -138,33 +179,29 @@ public final class DeferredResult<T> {
|
|||
return setResultInternal(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if this DeferredResult is no longer usable either
|
||||
* because it was previously set or because the underlying request expired.
|
||||
* <p>
|
||||
* The result may have been set with a call to {@link #setResult(Object)},
|
||||
* or {@link #setErrorResult(Object)}, or as a result of a timeout, if a
|
||||
* timeout result was provided to the constructor. The request may also
|
||||
* expire due to a timeout or network error.
|
||||
*/
|
||||
public boolean isSetOrExpired() {
|
||||
return ((this.result != RESULT_NONE) || this.expired);
|
||||
}
|
||||
DeferredResultProcessingInterceptor getInterceptor() {
|
||||
return new DeferredResultProcessingInterceptorAdapter() {
|
||||
|
||||
/**
|
||||
* Mark this instance expired so it may no longer be used.
|
||||
* @return the previous value of the expiration flag
|
||||
*/
|
||||
boolean expire() {
|
||||
synchronized (this) {
|
||||
boolean previous = this.expired;
|
||||
this.expired = true;
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public <S> void afterTimeout(NativeWebRequest request, DeferredResult<S> deferredResult) {
|
||||
if (timeoutCallback != null) {
|
||||
timeoutCallback.run();
|
||||
}
|
||||
if (DeferredResult.this.timeoutResult != RESULT_NONE) {
|
||||
setResultInternal(timeoutResult);
|
||||
}
|
||||
}
|
||||
|
||||
boolean applyTimeoutResult() {
|
||||
return (this.timeoutResult != RESULT_NONE) ? setResultInternal(this.timeoutResult) : false;
|
||||
@Override
|
||||
public <S> void afterCompletion(NativeWebRequest request, DeferredResult<S> deferredResult) {
|
||||
synchronized (this) {
|
||||
expired = true;
|
||||
}
|
||||
if (completionCallback != null) {
|
||||
completionCallback.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
|
@ -38,8 +36,8 @@ class DeferredResultInterceptorChain {
|
|||
private int preProcessingIndex = -1;
|
||||
|
||||
|
||||
public DeferredResultInterceptorChain(Collection<DeferredResultProcessingInterceptor> interceptors) {
|
||||
this.interceptors = new ArrayList<DeferredResultProcessingInterceptor>(interceptors);
|
||||
public DeferredResultInterceptorChain(List<DeferredResultProcessingInterceptor> interceptors) {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
public void applyPreProcess(NativeWebRequest request, DeferredResult<?> deferredResult) throws Exception {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public interface DeferredResultProcessingInterceptor {
|
|||
* Invoked from a container thread when an async request times out before
|
||||
* the {@code DeferredResult} has been set. Implementations may invoke
|
||||
* {@link DeferredResult#setResult(Object) setResult} or
|
||||
* {@link DeferredResult#setErrorResult(Object) to resume processing.
|
||||
* {@link DeferredResult#setErrorResult(Object) setErrorResult} to resume processing.
|
||||
*
|
||||
* @param request the current request
|
||||
* @param deferredResult the DeferredResult for the current request; if the
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.util.concurrent.Callable;
|
|||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
|
||||
/**
|
||||
* Holder for a {@link Callable}, a timeout value, and a task executor.
|
||||
|
|
@ -27,7 +28,7 @@ import org.springframework.util.Assert;
|
|||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*/
|
||||
public class AsyncTask<V> {
|
||||
public class MvcAsyncTask<V> {
|
||||
|
||||
private final Callable<V> callable;
|
||||
|
||||
|
|
@ -37,40 +38,51 @@ public class AsyncTask<V> {
|
|||
|
||||
private final AsyncTaskExecutor executor;
|
||||
|
||||
private Callable<V> timeoutCallback;
|
||||
|
||||
private Runnable completionCallback;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create an AsyncTask with a timeout value and a Callable.
|
||||
* @param timeout timeout value in milliseconds
|
||||
* Create an {@code MvcAsyncTask} wrapping the given {@link Callable}.
|
||||
* @param callable the callable for concurrent handling
|
||||
*/
|
||||
public AsyncTask(long timeout, Callable<V> callable) {
|
||||
this(timeout, null, null, callable);
|
||||
Assert.notNull(timeout, "Timeout must not be null");
|
||||
public MvcAsyncTask(Callable<V> callable) {
|
||||
this(null, null, null, callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AsyncTask with a timeout value, an executor name, and a Callable.
|
||||
* Create an {@code MvcAsyncTask} with a timeout value and a {@link Callable}.
|
||||
* @param timeout timeout value in milliseconds
|
||||
* @param callable the callable for concurrent handling
|
||||
*/
|
||||
public MvcAsyncTask(long timeout, Callable<V> callable) {
|
||||
this(timeout, null, null, callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@code MvcAsyncTask} with a timeout value, an executor name, and a {@link Callable}.
|
||||
* @param timeout timeout value in milliseconds; ignored if {@code null}
|
||||
* @param callable the callable for concurrent handling
|
||||
*/
|
||||
public AsyncTask(Long timeout, String executorName, Callable<V> callable) {
|
||||
public MvcAsyncTask(Long timeout, String executorName, Callable<V> callable) {
|
||||
this(timeout, null, executorName, callable);
|
||||
Assert.notNull(executor, "Executor name must not be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AsyncTask with a timeout value, an executor instance, and a Callable.
|
||||
* Create an {@code MvcAsyncTask} with a timeout value, an executor instance, and a Callable.
|
||||
* @param timeout timeout value in milliseconds; ignored if {@code null}
|
||||
* @param callable the callable for concurrent handling
|
||||
*/
|
||||
public AsyncTask(Long timeout, AsyncTaskExecutor executor, Callable<V> callable) {
|
||||
public MvcAsyncTask(Long timeout, AsyncTaskExecutor executor, Callable<V> callable) {
|
||||
this(timeout, executor, null, callable);
|
||||
Assert.notNull(executor, "Executor must not be null");
|
||||
}
|
||||
|
||||
private AsyncTask(Long timeout, AsyncTaskExecutor executor, String executorName, Callable<V> callable) {
|
||||
private MvcAsyncTask(Long timeout, AsyncTaskExecutor executor, String executorName, Callable<V> callable) {
|
||||
Assert.notNull(callable, "Callable must not be null");
|
||||
this.callable = callable;
|
||||
this.timeout = timeout;
|
||||
|
|
@ -111,11 +123,50 @@ public class AsyncTask<V> {
|
|||
|
||||
/**
|
||||
* A {@link BeanFactory} to use to resolve an executor name. Applications are
|
||||
* not expected to have to set this property when AsyncTask is used in a
|
||||
* not expected to have to set this property when {@code MvcAsyncTask} is used in a
|
||||
* Spring MVC controller.
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register code to invoke when the async request times out. This method is
|
||||
* called from a container thread when an async request times out before the
|
||||
* {@code Callable} has completed. The callback is executed in the same
|
||||
* thread and therefore should return without blocking. It may return an
|
||||
* alternative value to use, including an {@link Exception} or return
|
||||
* {@link CallableProcessingInterceptor#RESULT_NONE RESULT_NONE}.
|
||||
*/
|
||||
public void onTimeout(Callable<V> callback) {
|
||||
this.timeoutCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register code to invoke when the async request completes. This method is
|
||||
* called from a container thread when an async request completed for any
|
||||
* reason including timeout and network error.
|
||||
*/
|
||||
public void onCompletion(Runnable callback) {
|
||||
this.completionCallback = callback;
|
||||
}
|
||||
|
||||
CallableProcessingInterceptor getInterceptor() {
|
||||
return new CallableProcessingInterceptorAdapter() {
|
||||
|
||||
@Override
|
||||
public <T> Object afterTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
|
||||
return (timeoutCallback != null) ? timeoutCallback.call() : CallableProcessingInterceptor.RESULT_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception {
|
||||
if (completionCallback != null) {
|
||||
completionCallback.run();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ public class NoSupportAsyncWebRequest extends ServletWebRequest implements Async
|
|||
// ignored
|
||||
}
|
||||
|
||||
public void setTimeoutHandler(Runnable runnable) {
|
||||
public void addTimeoutHandler(Runnable runnable) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
|
|||
|
||||
private AtomicBoolean asyncCompleted = new AtomicBoolean(false);
|
||||
|
||||
private Runnable timeoutHandler;
|
||||
private final List<Runnable> timeoutHandlers = new ArrayList<Runnable>();
|
||||
|
||||
private final List<Runnable> completionHandlers = new ArrayList<Runnable>();
|
||||
|
||||
|
|
@ -73,8 +73,8 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
|
|||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public void setTimeoutHandler(Runnable timeoutHandler) {
|
||||
this.timeoutHandler = timeoutHandler;
|
||||
public void addTimeoutHandler(Runnable timeoutHandler) {
|
||||
this.timeoutHandlers.add(timeoutHandler);
|
||||
}
|
||||
|
||||
public void addCompletionHandler(Runnable runnable) {
|
||||
|
|
@ -127,8 +127,8 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
|
|||
}
|
||||
|
||||
public void onTimeout(AsyncEvent event) throws IOException {
|
||||
if (this.timeoutHandler != null) {
|
||||
this.timeoutHandler.run();
|
||||
for (Runnable handler : this.timeoutHandlers) {
|
||||
handler.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
|
@ -155,19 +157,27 @@ public final class WebAsyncManager {
|
|||
return this.concurrentResultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link CallableProcessingInterceptor} registered under the given key.
|
||||
* @param key the key
|
||||
* @return the interceptor registered under that key or {@code null}
|
||||
*/
|
||||
public CallableProcessingInterceptor getCallableInterceptor(Object key) {
|
||||
return this.callableInterceptors.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link DeferredResultProcessingInterceptor} registered under the given key.
|
||||
* @param key the key
|
||||
* @return the interceptor registered under that key or {@code null}
|
||||
*/
|
||||
public DeferredResultProcessingInterceptor getDeferredResultInterceptor(Object key) {
|
||||
return this.deferredResultInterceptors.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link CallableProcessingInterceptor} that will be applied
|
||||
* when concurrent request handling with a {@link Callable} starts.
|
||||
*
|
||||
* @param key a unique the key under which to register the interceptor
|
||||
* Register a {@link CallableProcessingInterceptor} under the given key.
|
||||
* @param key the key
|
||||
* @param interceptor the interceptor to register
|
||||
*/
|
||||
public void registerCallableInterceptor(Object key, CallableProcessingInterceptor interceptor) {
|
||||
|
|
@ -181,11 +191,8 @@ public final class WebAsyncManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Register a {@link DeferredResultProcessingInterceptor} that will be
|
||||
* applied when concurrent request handling with a {@link DeferredResult}
|
||||
* starts.
|
||||
*
|
||||
* @param key a unique the key under which to register the interceptor
|
||||
* Register a {@link DeferredResultProcessingInterceptor} under the given key.
|
||||
* @param key the key
|
||||
* @param interceptor the interceptor to register
|
||||
*/
|
||||
public void registerDeferredResultInterceptor(Object key, DeferredResultProcessingInterceptor interceptor) {
|
||||
|
|
@ -221,16 +228,47 @@ public final class WebAsyncManager {
|
|||
* @see #getConcurrentResult()
|
||||
* @see #getConcurrentResultContext()
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void startCallableProcessing(final Callable<?> callable, Object... processingContext) {
|
||||
Assert.notNull(callable, "Callable must not be null");
|
||||
startCallableProcessing(new MvcAsyncTask(callable), processingContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given {@link MvcAsyncTask} to configure the task executor as well as
|
||||
* the timeout value of the {@code AsyncWebRequest} before delegating to
|
||||
* {@link #startCallableProcessing(Callable, Object...)}.
|
||||
*
|
||||
* @param mvcAsyncTask an MvcAsyncTask containing the target {@code Callable}
|
||||
* @param processingContext additional context to save that can be accessed
|
||||
* via {@link #getConcurrentResultContext()}
|
||||
*/
|
||||
public void startCallableProcessing(final MvcAsyncTask<?> mvcAsyncTask, Object... processingContext) {
|
||||
Assert.notNull(mvcAsyncTask, "MvcAsyncTask must not be null");
|
||||
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
|
||||
|
||||
final CallableInterceptorChain chain = new CallableInterceptorChain(this.callableInterceptors.values());
|
||||
final Callable<?> callable = mvcAsyncTask.getCallable();
|
||||
|
||||
this.asyncWebRequest.setTimeoutHandler(new Runnable() {
|
||||
Long timeout = mvcAsyncTask.getTimeout();
|
||||
if (timeout != null) {
|
||||
this.asyncWebRequest.setTimeout(timeout);
|
||||
}
|
||||
|
||||
AsyncTaskExecutor executor = mvcAsyncTask.getExecutor();
|
||||
if (executor != null) {
|
||||
this.taskExecutor = executor;
|
||||
}
|
||||
|
||||
List<CallableProcessingInterceptor> interceptors = new ArrayList<CallableProcessingInterceptor>();
|
||||
interceptors.add(mvcAsyncTask.getInterceptor());
|
||||
interceptors.addAll(this.callableInterceptors.values());
|
||||
|
||||
final CallableInterceptorChain interceptorChain = new CallableInterceptorChain(interceptors);
|
||||
|
||||
this.asyncWebRequest.addTimeoutHandler(new Runnable() {
|
||||
public void run() {
|
||||
logger.debug("Processing timeout");
|
||||
Object result = chain.triggerAfterTimeout(asyncWebRequest, callable);
|
||||
Object result = interceptorChain.triggerAfterTimeout(asyncWebRequest, callable);
|
||||
if (result != CallableProcessingInterceptor.RESULT_NONE) {
|
||||
setConcurrentResultAndDispatch(result);
|
||||
}
|
||||
|
|
@ -239,7 +277,7 @@ public final class WebAsyncManager {
|
|||
|
||||
this.asyncWebRequest.addCompletionHandler(new Runnable() {
|
||||
public void run() {
|
||||
chain.triggerAfterCompletion(asyncWebRequest, callable);
|
||||
interceptorChain.triggerAfterCompletion(asyncWebRequest, callable);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -249,14 +287,14 @@ public final class WebAsyncManager {
|
|||
public void run() {
|
||||
Object result = null;
|
||||
try {
|
||||
chain.applyPreProcess(asyncWebRequest, callable);
|
||||
interceptorChain.applyPreProcess(asyncWebRequest, callable);
|
||||
result = callable.call();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
result = t;
|
||||
}
|
||||
finally {
|
||||
result = chain.applyPostProcess(asyncWebRequest, callable, result);
|
||||
result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result);
|
||||
}
|
||||
setConcurrentResultAndDispatch(result);
|
||||
}
|
||||
|
|
@ -282,32 +320,6 @@ public final class WebAsyncManager {
|
|||
asyncWebRequest.dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given {@link AsyncTask} to configure the task executor as well as
|
||||
* the timeout value of the {@code AsyncWebRequest} before delegating to
|
||||
* {@link #startCallableProcessing(Callable, Object...)}.
|
||||
*
|
||||
* @param asyncTask an asyncTask containing the target {@code Callable}
|
||||
* @param processingContext additional context to save that can be accessed
|
||||
* via {@link #getConcurrentResultContext()}
|
||||
*/
|
||||
public void startCallableProcessing(AsyncTask<?> asyncTask, Object... processingContext) {
|
||||
Assert.notNull(asyncTask, "AsyncTask must not be null");
|
||||
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
|
||||
|
||||
Long timeout = asyncTask.getTimeout();
|
||||
if (timeout != null) {
|
||||
this.asyncWebRequest.setTimeout(timeout);
|
||||
}
|
||||
|
||||
AsyncTaskExecutor executor = asyncTask.getExecutor();
|
||||
if (executor != null) {
|
||||
this.taskExecutor = executor;
|
||||
}
|
||||
|
||||
startCallableProcessing(asyncTask.getCallable(), processingContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start concurrent request processing and initialize the given
|
||||
* {@link DeferredResult} with a {@link DeferredResultHandler} that saves
|
||||
|
|
@ -329,41 +341,41 @@ public final class WebAsyncManager {
|
|||
Assert.notNull(deferredResult, "DeferredResult must not be null");
|
||||
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
|
||||
|
||||
Long timeout = deferredResult.getTimeoutMilliseconds();
|
||||
Long timeout = deferredResult.getTimeoutValue();
|
||||
if (timeout != null) {
|
||||
this.asyncWebRequest.setTimeout(timeout);
|
||||
}
|
||||
|
||||
final DeferredResultInterceptorChain chain =
|
||||
new DeferredResultInterceptorChain(this.deferredResultInterceptors.values());
|
||||
List<DeferredResultProcessingInterceptor> interceptors = new ArrayList<DeferredResultProcessingInterceptor>();
|
||||
interceptors.add(deferredResult.getInterceptor());
|
||||
interceptors.addAll(this.deferredResultInterceptors.values());
|
||||
|
||||
this.asyncWebRequest.setTimeoutHandler(new Runnable() {
|
||||
final DeferredResultInterceptorChain interceptorChain = new DeferredResultInterceptorChain(interceptors);
|
||||
|
||||
this.asyncWebRequest.addTimeoutHandler(new Runnable() {
|
||||
public void run() {
|
||||
if (!deferredResult.applyTimeoutResult()) {
|
||||
try {
|
||||
chain.triggerAfterTimeout(asyncWebRequest, deferredResult);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
setConcurrentResultAndDispatch(t);
|
||||
}
|
||||
try {
|
||||
interceptorChain.triggerAfterTimeout(asyncWebRequest, deferredResult);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
setConcurrentResultAndDispatch(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.asyncWebRequest.addCompletionHandler(new Runnable() {
|
||||
public void run() {
|
||||
deferredResult.expire();
|
||||
chain.triggerAfterCompletion(asyncWebRequest, deferredResult);
|
||||
interceptorChain.triggerAfterCompletion(asyncWebRequest, deferredResult);
|
||||
}
|
||||
});
|
||||
|
||||
startAsyncProcessing(processingContext);
|
||||
|
||||
try {
|
||||
chain.applyPreProcess(this.asyncWebRequest, deferredResult);
|
||||
interceptorChain.applyPreProcess(this.asyncWebRequest, deferredResult);
|
||||
deferredResult.setResultHandler(new DeferredResultHandler() {
|
||||
public void handleResult(Object result) {
|
||||
result = chain.applyPostProcess(asyncWebRequest, deferredResult, result);
|
||||
result = interceptorChain.applyPostProcess(asyncWebRequest, deferredResult, result);
|
||||
setConcurrentResultAndDispatch(result);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.web.context.request.async;
|
|||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
|
@ -80,27 +81,42 @@ public class DeferredResultTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void setExpired() {
|
||||
DeferredResult<String> result = new DeferredResult<String>();
|
||||
assertFalse(result.isSetOrExpired());
|
||||
public void onCompletion() throws Exception {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
DeferredResult<String> result = new DeferredResult<String>();
|
||||
result.onCompletion(new Runnable() {
|
||||
public void run() {
|
||||
sb.append("completion event");
|
||||
}
|
||||
});
|
||||
|
||||
result.getInterceptor().afterCompletion(null, null);
|
||||
|
||||
result.expire();
|
||||
assertTrue(result.isSetOrExpired());
|
||||
assertFalse(result.setResult("hello"));
|
||||
assertEquals("completion event", sb.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyTimeoutResult() {
|
||||
public void onTimeout() throws Exception {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
DeferredResultHandler handler = createMock(DeferredResultHandler.class);
|
||||
handler.handleResult("timed out");
|
||||
handler.handleResult("timeout result");
|
||||
replay(handler);
|
||||
|
||||
DeferredResult<String> result = new DeferredResult<String>(null, "timed out");
|
||||
DeferredResult<String> result = new DeferredResult<String>(null, "timeout result");
|
||||
result.setResultHandler(handler);
|
||||
result.onTimeout(new Runnable() {
|
||||
public void run() {
|
||||
sb.append("timeout event");
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(result.applyTimeoutResult());
|
||||
assertFalse("Shouldn't be able to set result after timeout", result.setResult("hello"));
|
||||
result.getInterceptor().afterTimeout(null, null);
|
||||
|
||||
assertEquals("timeout event", sb.toString());
|
||||
assertFalse("Should not be able to set result a second time", result.setResult("hello"));
|
||||
verify(handler);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public class StandardServletAsyncWebRequestTests {
|
|||
timeoutHandler.run();
|
||||
replay(timeoutHandler);
|
||||
|
||||
this.asyncRequest.setTimeoutHandler(timeoutHandler);
|
||||
this.asyncRequest.addTimeoutHandler(timeoutHandler);
|
||||
this.asyncRequest.onTimeout(new AsyncEvent(null));
|
||||
|
||||
verify(timeoutHandler);
|
||||
|
|
|
|||
|
|
@ -208,13 +208,13 @@ public class WebAsyncManagerTests {
|
|||
replay(executor);
|
||||
|
||||
this.asyncWebRequest.setTimeout(1000L);
|
||||
this.asyncWebRequest.setTimeoutHandler(EasyMock.<Runnable>anyObject());
|
||||
this.asyncWebRequest.addTimeoutHandler(EasyMock.<Runnable>anyObject());
|
||||
this.asyncWebRequest.addCompletionHandler(EasyMock.<Runnable>anyObject());
|
||||
this.asyncWebRequest.startAsync();
|
||||
replay(this.asyncWebRequest);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
AsyncTask<Object> asyncTask = new AsyncTask<Object>(1000L, executor, createMock(Callable.class));
|
||||
MvcAsyncTask<Object> asyncTask = new MvcAsyncTask<Object>(1000L, executor, createMock(Callable.class));
|
||||
this.asyncManager.startCallableProcessing(asyncTask);
|
||||
|
||||
verify(executor, this.asyncWebRequest);
|
||||
|
|
@ -311,7 +311,7 @@ public class WebAsyncManagerTests {
|
|||
}
|
||||
|
||||
private void setupDefaultAsyncScenario() {
|
||||
this.asyncWebRequest.setTimeoutHandler((Runnable) notNull());
|
||||
this.asyncWebRequest.addTimeoutHandler((Runnable) notNull());
|
||||
this.asyncWebRequest.addCompletionHandler((Runnable) notNull());
|
||||
this.asyncWebRequest.startAsync();
|
||||
expect(this.asyncWebRequest.isAsyncComplete()).andReturn(false);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.web.context.request.async;
|
||||
|
||||
import static org.springframework.web.context.request.async.CallableProcessingInterceptor.RESULT_NONE;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.createStrictMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
|
|
@ -25,6 +24,8 @@ import static org.easymock.EasyMock.replay;
|
|||
import static org.easymock.EasyMock.verify;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.web.context.request.async.CallableProcessingInterceptor.RESULT_NONE;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
|
|
@ -94,7 +95,27 @@ public class WebAsyncManagerTimeoutTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void startCallableProcessingTimeoutAndResume() throws Exception {
|
||||
public void startCallableProcessingTimeoutAndResumeThroughCallback() throws Exception {
|
||||
|
||||
StubCallable callable = new StubCallable();
|
||||
MvcAsyncTask<Object> mvcAsyncTask = new MvcAsyncTask<Object>(callable);
|
||||
mvcAsyncTask.onTimeout(new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
return 7;
|
||||
}
|
||||
});
|
||||
|
||||
this.asyncManager.startCallableProcessing(mvcAsyncTask);
|
||||
|
||||
this.asyncWebRequest.onTimeout(ASYNC_EVENT);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(7, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startCallableProcessingTimeoutAndResumeThroughInterceptor() throws Exception {
|
||||
|
||||
StubCallable callable = new StubCallable();
|
||||
|
||||
|
|
@ -107,6 +128,7 @@ public class WebAsyncManagerTimeoutTests {
|
|||
|
||||
this.asyncWebRequest.onTimeout(ASYNC_EVENT);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(22, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
|
||||
|
|
@ -128,6 +150,7 @@ public class WebAsyncManagerTimeoutTests {
|
|||
|
||||
this.asyncWebRequest.onTimeout(ASYNC_EVENT);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(exception, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
|
||||
|
|
@ -161,25 +184,38 @@ public class WebAsyncManagerTimeoutTests {
|
|||
public void startDeferredResultProcessingTimeoutAndResumeWithDefaultResult() throws Exception {
|
||||
|
||||
DeferredResult<Integer> deferredResult = new DeferredResult<Integer>(null, 23);
|
||||
|
||||
DeferredResultProcessingInterceptor interceptor = new DeferredResultProcessingInterceptorAdapter() {
|
||||
public <T> void afterTimeout(NativeWebRequest request, DeferredResult<T> result) throws Exception {
|
||||
result.setErrorResult("should not get here");
|
||||
}
|
||||
};
|
||||
|
||||
this.asyncManager.registerDeferredResultInterceptor("interceptor", interceptor);
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
AsyncEvent event = null;
|
||||
this.asyncWebRequest.onTimeout(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(23, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingTimeoutAndResumeWithInterceptor() throws Exception {
|
||||
public void startDeferredResultProcessingTimeoutAndResumeThroughCallback() throws Exception {
|
||||
|
||||
final DeferredResult<Integer> deferredResult = new DeferredResult<Integer>();
|
||||
deferredResult.onTimeout(new Runnable() {
|
||||
public void run() {
|
||||
deferredResult.setResult(23);
|
||||
}
|
||||
});
|
||||
|
||||
this.asyncManager.startDeferredResultProcessing(deferredResult);
|
||||
|
||||
AsyncEvent event = null;
|
||||
this.asyncWebRequest.onTimeout(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(23, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startDeferredResultProcessingTimeoutAndResumeThroughInterceptor() throws Exception {
|
||||
|
||||
DeferredResult<Integer> deferredResult = new DeferredResult<Integer>();
|
||||
|
||||
|
|
@ -195,6 +231,7 @@ public class WebAsyncManagerTimeoutTests {
|
|||
AsyncEvent event = null;
|
||||
this.asyncWebRequest.onTimeout(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(23, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
|
@ -217,6 +254,7 @@ public class WebAsyncManagerTimeoutTests {
|
|||
AsyncEvent event = null;
|
||||
this.asyncWebRequest.onTimeout(event);
|
||||
|
||||
assertTrue(this.asyncManager.hasConcurrentResult());
|
||||
assertEquals(exception, this.asyncManager.getConcurrentResult());
|
||||
assertEquals("/test", ((MockAsyncContext) this.servletRequest.getAsyncContext()).getDispatchedPath());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -905,7 +905,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
|
|||
initContextHolders(request, localeContext, requestAttributes);
|
||||
|
||||
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
|
||||
asyncManager.registerCallableInterceptor(this.getClass().getName(), createRequestBindingInterceptor(request));
|
||||
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), getRequestBindingInterceptor(request));
|
||||
|
||||
try {
|
||||
doService(request, response);
|
||||
|
|
@ -988,7 +988,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
|
|||
}
|
||||
}
|
||||
|
||||
private CallableProcessingInterceptor createRequestBindingInterceptor(final HttpServletRequest request) {
|
||||
private CallableProcessingInterceptor getRequestBindingInterceptor(final HttpServletRequest request) {
|
||||
return new CallableProcessingInterceptorAdapter() {
|
||||
@Override
|
||||
public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import java.util.concurrent.Callable;
|
|||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.async.AsyncTask;
|
||||
import org.springframework.web.context.request.async.MvcAsyncTask;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
|
||||
|
|
@ -50,7 +50,7 @@ public class AsyncSupportConfigurer {
|
|||
/**
|
||||
* Set the default {@link AsyncTaskExecutor} to use when a controller method
|
||||
* returns a {@link Callable}. Controller methods can override this default on
|
||||
* a per-request basis by returning an {@link AsyncTask}.
|
||||
* a per-request basis by returning an {@link MvcAsyncTask}.
|
||||
*
|
||||
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used and it's
|
||||
* highly recommended to change that default in production since the simple
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
|||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.async.AsyncTask;
|
||||
import org.springframework.web.context.request.async.MvcAsyncTask;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Handles return values of type {@link AsyncTask}.
|
||||
* Handles return values of type {@link MvcAsyncTask}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
|
|
@ -41,7 +41,7 @@ public class AsyncTaskMethodReturnValueHandler implements HandlerMethodReturnVal
|
|||
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
Class<?> paramType = returnType.getParameterType();
|
||||
return AsyncTask.class.isAssignableFrom(paramType);
|
||||
return MvcAsyncTask.class.isAssignableFrom(paramType);
|
||||
}
|
||||
|
||||
public void handleReturnValue(Object returnValue,
|
||||
|
|
@ -52,9 +52,9 @@ public class AsyncTaskMethodReturnValueHandler implements HandlerMethodReturnVal
|
|||
return;
|
||||
}
|
||||
|
||||
AsyncTask<?> asyncTask = (AsyncTask<?>) returnValue;
|
||||
asyncTask.setBeanFactory(this.beanFactory);
|
||||
WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(asyncTask, mavContainer);
|
||||
MvcAsyncTask<?> mvcAsyncTask = (MvcAsyncTask<?>) returnValue;
|
||||
mvcAsyncTask.setBeanFactory(this.beanFactory);
|
||||
WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(mvcAsyncTask, mavContainer);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
|||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.request.async.AsyncTask;
|
||||
import org.springframework.web.context.request.async.MvcAsyncTask;
|
||||
import org.springframework.web.context.request.async.AsyncWebRequest;
|
||||
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
|
||||
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
|
||||
|
|
@ -350,7 +350,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
|
|||
/**
|
||||
* Set the default {@link AsyncTaskExecutor} to use when a controller method
|
||||
* return a {@link Callable}. Controller methods can override this default on
|
||||
* a per-request basis by returning an {@link AsyncTask}.
|
||||
* a per-request basis by returning an {@link MvcAsyncTask}.
|
||||
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used.
|
||||
* It's recommended to change that default in production as the simple executor
|
||||
* does not re-use threads.
|
||||
|
|
|
|||
Loading…
Reference in New Issue