Add config options for MVC async interceptors

The MVC namespace and the MVC Java config now allow configuring
CallableProcessingInterceptor and DeferredResultProcessingInterceptor
instances.

Issue: SPR-9914
This commit is contained in:
Rossen Stoyanchev
2012-10-26 21:29:54 -04:00
parent e4a13cd4ad
commit e14ba9dec3
10 changed files with 240 additions and 16 deletions

View File

@@ -174,6 +174,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, source, parserContext);
String asyncTimeout = getAsyncTimeout(element, source, parserContext);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element, source, parserContext);
ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
@@ -197,6 +199,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
@@ -337,6 +341,40 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
return null;
}
private ManagedList<?> getCallableInterceptors(Element element, Object source, ParserContext parserContext) {
ManagedList<? super Object> interceptors = new ManagedList<Object>();
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
if (asyncElement != null) {
Element interceptorsElement = DomUtils.getChildElementByTagName(asyncElement, "callable-interceptors");
if (interceptorsElement != null) {
interceptors.setSource(source);
for (Element converter : DomUtils.getChildElementsByTagName(interceptorsElement, "bean")) {
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
interceptors.add(beanDef);
}
}
}
return interceptors;
}
private ManagedList<?> getDeferredResultInterceptors(Element element, Object source, ParserContext parserContext) {
ManagedList<? super Object> interceptors = new ManagedList<Object>();
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
if (asyncElement != null) {
Element interceptorsElement = DomUtils.getChildElementByTagName(asyncElement, "deferred-result-interceptors");
if (interceptorsElement != null) {
interceptors.setSource(source);
for (Element converter : DomUtils.getChildElementsByTagName(interceptorsElement, "bean")) {
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
interceptors.add(beanDef);
}
}
}
return interceptors;
}
private ManagedList<?> getArgumentResolvers(Element element, Object source, ParserContext parserContext) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
if (resolversElement != null) {

View File

@@ -15,11 +15,18 @@
*/
package org.springframework.web.servlet.config.annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
/**
* Helps with configuring a options for asynchronous request processing.
@@ -33,6 +40,13 @@ public class AsyncSupportConfigurer {
private Long timeout;
private final List<CallableProcessingInterceptor> callableInterceptors =
new ArrayList<CallableProcessingInterceptor>();
private final List<DeferredResultProcessingInterceptor> deferredResultInterceptors =
new ArrayList<DeferredResultProcessingInterceptor>();
/**
* Set the default {@link AsyncTaskExecutor} to use when a controller method
* returns a {@link Callable}. Controller methods can override this default on
@@ -64,6 +78,31 @@ public class AsyncSupportConfigurer {
return this;
}
/**
* Configure lifecycle intercepters with callbacks around concurrent request
* execution that starts when a controller returns a
* {@link java.util.concurrent.Callable}.
*
* @param interceptors the interceptors to register
*/
public AsyncSupportConfigurer registerCallableInterceptors(CallableProcessingInterceptor... interceptors) {
Assert.notNull(interceptors, "Interceptors are required");
this.callableInterceptors.addAll(Arrays.asList(interceptors));
return this;
}
/**
* Configure lifecycle intercepters with callbacks around concurrent request
* execution that starts when a controller returns a {@link DeferredResult}.
*
* @param interceptors the interceptors to register
*/
public AsyncSupportConfigurer registerDeferredResultInterceptors(DeferredResultProcessingInterceptor... interceptors) {
Assert.notNull(interceptors, "Interceptors are required");
this.deferredResultInterceptors.addAll(Arrays.asList(interceptors));
return this;
}
protected AsyncTaskExecutor getTaskExecutor() {
return this.taskExecutor;
}
@@ -72,4 +111,12 @@ public class AsyncSupportConfigurer {
return this.timeout;
}
protected List<CallableProcessingInterceptor> getCallableInterceptors() {
return this.callableInterceptors;
}
protected List<DeferredResultProcessingInterceptor> getDeferredResultInterceptors() {
return this.deferredResultInterceptors;
}
}

View File

@@ -378,6 +378,8 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
return adapter;
}

View File

@@ -48,6 +48,7 @@ import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.accept.ContentNegotiationManager;
@@ -64,6 +65,8 @@ 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.AsyncWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.method.ControllerAdviceBean;
@@ -135,6 +138,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
private Long asyncRequestTimeout;
private final Map<Object, CallableProcessingInterceptor> callableInterceptors =
new LinkedHashMap<Object, CallableProcessingInterceptor>();
private final Map<Object, DeferredResultProcessingInterceptor> deferredResultInterceptors =
new LinkedHashMap<Object, DeferredResultProcessingInterceptor>();
private boolean ignoreDefaultModelOnRedirect = false;
private int cacheSecondsForSessionAttributeHandlers = 0;
@@ -363,6 +372,34 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
this.asyncRequestTimeout = timeout;
}
/**
* Configure {@code CallableProcessingInterceptor}'s to register on async requests.
* @param interceptors the interceptors to register
*/
public void setCallableInterceptors(List<CallableProcessingInterceptor> interceptors) {
Assert.notNull(interceptors);
for (int index = 0 ; index < interceptors.size(); index++) {
CallableProcessingInterceptor interceptor = interceptors.get(index);
this.callableInterceptors.put(getInterceptorKey(interceptor, index), interceptor);
}
}
/**
* Configure {@code DeferredResultProcessingInterceptor}'s to register on async requests.
* @param interceptors the interceptors to register
*/
public void setDeferredResultInterceptors(List<DeferredResultProcessingInterceptor> interceptors) {
Assert.notNull(interceptors);
for (int index = 0 ; index < interceptors.size(); index++) {
DeferredResultProcessingInterceptor interceptor = interceptors.get(index);
this.deferredResultInterceptors.put(getInterceptorKey(interceptor, index), interceptor);
}
}
private String getInterceptorKey(Object interceptor, int index) {
return this.hashCode() + ":" + interceptor.getClass().getName() + "#" + index;
}
/**
* By default the content of the "default" model is used both during
* rendering and redirect scenarios. Alternatively a controller method
@@ -703,6 +740,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerAllCallableInterceptors(this.callableInterceptors);
asyncManager.registerAllDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();