Add beforeConcurrentHandling support
Previously CallableProcessingInterceptor and DeferredResultProcessingInterceptor did not have support for capturing the state of the original Thread just prior to processing. This made it difficult to transfer the state of one Thread (i.e. ThreadLocal) to the Thread used to process the Callable. This commit adds a new method to CallableProcessingInterceptor and DeferredResultProcessingInterceptor named beforeConcurrentHandling which will be invoked on the original Thread used to submit the Callable or DeferredResult. This means the state of the original Thread can be captured in beforeConcurrentHandling and transfered to the new Thread in preProcess. Issue: SPR-10052
This commit is contained in:
committed by
Rossen Stoyanchev
parent
0d73d199ec
commit
1e62ad8665
@@ -26,6 +26,7 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* Assists with the invocation of {@link CallableProcessingInterceptor}'s.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
class CallableInterceptorChain {
|
||||
@@ -41,6 +42,12 @@ class CallableInterceptorChain {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
public void applyBeforeConcurrentHandling(NativeWebRequest request, Callable<?> task) throws Exception {
|
||||
for (CallableProcessingInterceptor interceptor : this.interceptors) {
|
||||
interceptor.beforeConcurrentHandling(request, task);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyPreProcess(NativeWebRequest request, Callable<?> task) throws Exception {
|
||||
for (CallableProcessingInterceptor interceptor : this.interceptors) {
|
||||
interceptor.preProcess(request, task);
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* can select a value to be used to resume processing.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface CallableProcessingInterceptor {
|
||||
@@ -47,6 +48,24 @@ public interface CallableProcessingInterceptor {
|
||||
|
||||
static final Object RESPONSE_HANDLED = new Object();
|
||||
|
||||
/**
|
||||
* Invoked <em>before</em> the start of concurrent handling in the original
|
||||
* thread in which the {@code Callable} is submitted for concurrent handling.
|
||||
*
|
||||
* <p>
|
||||
* This is useful for capturing the state of the current thread just prior to
|
||||
* invoking the {@link Callable}. Once the state is captured, it can then be
|
||||
* transfered to the new {@link Thread} in
|
||||
* {@link #preProcess(NativeWebRequest, Callable)}. Capturing the state of
|
||||
* Spring Security's SecurityContextHolder and migrating it to the new Thread
|
||||
* is a concrete example of where this is useful.
|
||||
* </p>
|
||||
*
|
||||
* @param request the current request
|
||||
* @param task the task for the current async request
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
<T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception;
|
||||
|
||||
/**
|
||||
* Invoked <em>after</em> the start of concurrent handling in the async
|
||||
|
||||
@@ -24,10 +24,17 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* for simplified implementation of individual methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class CallableProcessingInterceptorAdapter implements CallableProcessingInterceptor {
|
||||
|
||||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,12 @@ class DeferredResultInterceptorChain {
|
||||
this.interceptors = interceptors;
|
||||
}
|
||||
|
||||
public void applyBeforeConcurrentHandling(NativeWebRequest request, DeferredResult<?> deferredResult) throws Exception {
|
||||
for (DeferredResultProcessingInterceptor interceptor : this.interceptors) {
|
||||
interceptor.beforeConcurrentHandling(request, deferredResult);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyPreProcess(NativeWebRequest request, DeferredResult<?> deferredResult) throws Exception {
|
||||
for (DeferredResultProcessingInterceptor interceptor : this.interceptors) {
|
||||
interceptor.preProcess(request, deferredResult);
|
||||
|
||||
@@ -36,10 +36,22 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* method can set the {@code DeferredResult} in order to resume processing.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface DeferredResultProcessingInterceptor {
|
||||
|
||||
/**
|
||||
* Invoked immediately before the start of concurrent handling, in the same
|
||||
* thread that started it. This method may be used to capture state just prior
|
||||
* to the start of concurrent processing with the given {@code DeferredResult}.
|
||||
*
|
||||
* @param request the current request
|
||||
* @param deferredResult the DeferredResult for the current request
|
||||
* @throws Exception in case of errors
|
||||
*/
|
||||
<T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception;
|
||||
|
||||
/**
|
||||
* Invoked immediately after the start of concurrent handling, in the same
|
||||
* thread that started it. This method may be used to detect the start of
|
||||
|
||||
@@ -22,10 +22,17 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* interface for simplified implementation of individual methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class DeferredResultProcessingInterceptorAdapter implements DeferredResultProcessingInterceptor {
|
||||
|
||||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
|
||||
@@ -249,12 +249,13 @@ public final class WebAsyncManager {
|
||||
* @param callable a unit of work to be executed asynchronously
|
||||
* @param processingContext additional context to save that can be accessed
|
||||
* via {@link #getConcurrentResultContext()}
|
||||
* @throws Exception If concurrent processing failed to start
|
||||
*
|
||||
* @see #getConcurrentResult()
|
||||
* @see #getConcurrentResultContext()
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void startCallableProcessing(final Callable<?> callable, Object... processingContext) {
|
||||
public void startCallableProcessing(final Callable<?> callable, Object... processingContext) throws Exception {
|
||||
Assert.notNull(callable, "Callable must not be null");
|
||||
startCallableProcessing(new WebAsyncTask(callable), processingContext);
|
||||
}
|
||||
@@ -267,8 +268,9 @@ public final class WebAsyncManager {
|
||||
* @param webAsyncTask an WebAsyncTask containing the target {@code Callable}
|
||||
* @param processingContext additional context to save that can be accessed
|
||||
* via {@link #getConcurrentResultContext()}
|
||||
* @throws Exception If concurrent processing failed to start
|
||||
*/
|
||||
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext) {
|
||||
public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext) throws Exception {
|
||||
Assert.notNull(webAsyncTask, "WebAsyncTask must not be null");
|
||||
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
|
||||
|
||||
@@ -306,6 +308,8 @@ public final class WebAsyncManager {
|
||||
}
|
||||
});
|
||||
|
||||
interceptorChain.applyBeforeConcurrentHandling(asyncWebRequest, callable);
|
||||
|
||||
startAsyncProcessing(processingContext);
|
||||
|
||||
this.taskExecutor.submit(new Runnable() {
|
||||
@@ -356,12 +360,13 @@ public final class WebAsyncManager {
|
||||
* @param deferredResult the DeferredResult instance to initialize
|
||||
* @param processingContext additional context to save that can be accessed
|
||||
* via {@link #getConcurrentResultContext()}
|
||||
* @throws Exception If concurrent processing failed to start
|
||||
*
|
||||
* @see #getConcurrentResult()
|
||||
* @see #getConcurrentResultContext()
|
||||
*/
|
||||
public void startDeferredResultProcessing(
|
||||
final DeferredResult<?> deferredResult, Object... processingContext) {
|
||||
final DeferredResult<?> deferredResult, Object... processingContext) throws Exception {
|
||||
|
||||
Assert.notNull(deferredResult, "DeferredResult must not be null");
|
||||
Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
|
||||
@@ -395,6 +400,8 @@ public final class WebAsyncManager {
|
||||
}
|
||||
});
|
||||
|
||||
interceptorChain.applyBeforeConcurrentHandling(asyncWebRequest, deferredResult);
|
||||
|
||||
startAsyncProcessing(processingContext);
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user