Add async options to MVC namespace and Java config

The MVC Java config method to implement is
WebMvcConfigurer.configureAsyncSupport(AsyncSupportConfigurer)

The MVC namespace element is:
<mvc:annotation-driven>
  <mvc:async-support default-timeout="2500" task-executor="myExecutor" />
</mvc:annotation-driven>

Issue: SPR-9694
This commit is contained in:
Rossen Stoyanchev
2012-08-17 17:05:16 -04:00
parent 4f55518290
commit 9c8c967caa
14 changed files with 552 additions and 257 deletions

View File

@@ -174,6 +174,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
ManagedList<?> argumentResolvers = getArgumentResolvers(element, source, parserContext);
ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, source, parserContext);
String asyncTimeout = getAsyncTimeout(element, source, parserContext);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element, source, parserContext);
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
@@ -191,6 +193,12 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
@@ -318,6 +326,21 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
}
}
private String getAsyncTimeout(Element element, Object source, ParserContext parserContext) {
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
return (asyncElement != null) ? asyncElement.getAttribute("default-timeout") : null;
}
private RuntimeBeanReference getAsyncExecutor(Element element, Object source, ParserContext parserContext) {
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
if (asyncElement != null) {
if (asyncElement.hasAttribute("task-executor")) {
return new RuntimeBeanReference(asyncElement.getAttribute("task-executor"));
}
}
return null;
}
private ManagedList<?> getArgumentResolvers(Element element, Object source, ParserContext parserContext) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
if (resolversElement != null) {

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.config.annotation;
import java.util.concurrent.Callable;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.web.context.request.async.AsyncTask;
/**
* Helps with configuring a options for asynchronous request processing.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class AsyncSupportConfigurer {
private AsyncTaskExecutor taskExecutor;
private Long timeout;
/**
* 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}.
*
* <p>By default a {@link SimpleAsyncTaskExecutor} instance is used and it's
* highly recommended to change that default in production since the simple
* executor does not re-use threads.
*
* @param taskExecutor the task executor instance to use by default
*/
public AsyncSupportConfigurer setTaskExecutor(AsyncTaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
return this;
}
/**
* Specify the amount of time, in milliseconds, before asynchronous request
* handling times out. In Servlet 3, the timeout begins after the main request
* processing thread has exited and ends when the request is dispatched again
* for further processing of the concurrently produced result.
* <p>If this value is not set, the default timeout of the underlying
* implementation is used, e.g. 10 seconds on Tomcat with Servlet 3.
*
* @param timeout the timeout value in milliseconds
*/
public AsyncSupportConfigurer setDefaultTimeout(long timeout) {
this.timeout = timeout;
return this;
}
protected AsyncTaskExecutor getTaskExecutor() {
return this.taskExecutor;
}
protected Long getTimeout() {
return this.timeout;
}
}

View File

@@ -60,6 +60,11 @@ public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
this.configurers.configureContentNegotiation(configurer);
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
this.configurers.configureAsyncSupport(configurer);
}
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);

View File

@@ -354,6 +354,17 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
adapter.setWebBindingInitializer(webBindingInitializer);
adapter.setCustomArgumentResolvers(argumentResolvers);
adapter.setCustomReturnValueHandlers(returnValueHandlers);
AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
configureAsyncSupport(configurer);
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
return adapter;
}
@@ -516,6 +527,13 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
protected void addFormatters(FormatterRegistry registry) {
}
/**
* Override this method to configure asynchronous request processing options.
* @see AsyncSupportConfigurer
*/
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
/**
* Returns a {@link HttpRequestHandlerAdapter} for processing requests
* with {@link HttpRequestHandler}s.

View File

@@ -74,6 +74,11 @@ public interface WebMvcConfigurer {
*/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/**
* Configure asynchronous request handling options.
*/
void configureAsyncSupport(AsyncSupportConfigurer configurer);
/**
* Add resolvers to support custom controller method argument types.
* <p>This does not override the built-in support for resolving handler

View File

@@ -64,6 +64,13 @@ public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
/**
* {@inheritDoc}
* <p>This implementation is empty.
*/
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
/**
* {@inheritDoc}
* <p>This implementation is empty.

View File

@@ -55,6 +55,12 @@ class WebMvcConfigurerComposite implements WebMvcConfigurer {
}
}
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.configureAsyncSupport(configurer);
}
}
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.configureMessageConverters(converters);

View File

@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
@@ -62,6 +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.AsyncWebRequest;
import org.springframework.web.context.request.async.AsyncWebUtils;
import org.springframework.web.context.request.async.WebAsyncManager;
@@ -400,26 +402,28 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
}
/**
* Set the AsyncTaskExecutor to use when a controller method returns a
* {@code Callable}.
* <p>The default instance type is a {@link SimpleAsyncTaskExecutor}.
* It's recommended to change that default in production as the simple
* executor does not re-use threads.
* 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}.
* <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.
*/
public void setAsyncTaskExecutor(AsyncTaskExecutor taskExecutor) {
public void setTaskExecutor(AsyncTaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
/**
* Set the timeout for asynchronous request processing in milliseconds.
* When the timeout begins depends on the underlying async technology.
* With the Servlet 3 async support the timeout begins after the main
* processing thread has exited and has been returned to the container pool.
* <p>If a value is not provided, the default timeout of the underlying
* async technology is used (10 seconds on Tomcat with Servlet 3 async).
* Specify the amount of time, in milliseconds, before concurrent handling
* should time out. In Servlet 3, the timeout begins after the main request
* processing thread has exited and ends when the request is dispatched again
* for further processing of the concurrently produced result.
* <p>If this value is not set, the default timeout of the underlying
* implementation is used, e.g. 10 seconds on Tomcat with Servlet 3.
* @param timeout the timeout value in milliseconds
*/
public void setAsyncRequestTimeout(long asyncRequestTimeout) {
this.asyncRequestTimeout = asyncRequestTimeout;
public void setAsyncRequestTimeout(long timeout) {
this.asyncRequestTimeout = timeout;
}
/**