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;
}
/**

View File

@@ -90,6 +90,38 @@
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="async-support" minOccurs="0">
<xsd:annotation>
<xsd:documentation><![CDATA[
Configure options for asynchronous request processing.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="task-executor" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.core.task.AsyncTaskExecutor"><![CDATA[
The bean name of a default 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 AsyncTask.
By default a SimpleAsyncTaskExecutor is used which does not re-use threads and is not recommended for production.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java:org.springframework.core.task.AsyncTaskExecutor" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="default-timeout" type="xsd:long">
<xsd:annotation>
<xsd:documentation><![CDATA[
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.
If this value is not set, the default timeout of the underlying implementation is used, e.g. 10 seconds on Tomcat with Servlet 3.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
</xsd:all>
<xsd:attribute name="conversion-service" type="xsd:string">
<xsd:annotation>