From e09bdb31cc4d8e77a3157a9ecff3d29750a8614a Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 11 Dec 2012 23:06:26 -0500 Subject: [PATCH] Add reference documentation for async web requests Issue: SPR-9400 --- src/reference/docbook/mvc.xml | 235 +++++++++++++++++++++++++++ src/reference/docbook/new-in-3.2.xml | 7 +- 2 files changed, 238 insertions(+), 4 deletions(-) diff --git a/src/reference/docbook/mvc.xml b/src/reference/docbook/mvc.xml index 2363b808ea7..3007fd9b76f 100644 --- a/src/reference/docbook/mvc.xml +++ b/src/reference/docbook/mvc.xml @@ -1567,6 +1567,18 @@ public String processSubmit(@ModelAttribute("pet") Pet pet linkend="mvc-ann-httpentity" />. + + A Callable<?> can + be returned when the application wants to produce the return + value asynchronously in a thread managed by Spring MVC. + + + + A DeferredResult<?> can + be returned when the application wants to produce the return + value from a thread of its own choosing. + + Any other return type is considered to be a single model attribute to be exposed to the view, using the attribute name @@ -1576,6 +1588,7 @@ public String processSubmit(@ModelAttribute("pet") Pet pet objects and the results of @ModelAttribute annotated reference data accessor methods. + @@ -2359,6 +2372,228 @@ public String myHandleMethod(WebRequest webRequest, Model model) { +
+ Asynchronous Request Processing + + Spring MVC 3.2 introduced Servlet 3 based asynchronous request + processing. Instead of returning a value, as usual, a controller method + can now return a java.util.concurrent.Callable + and produce the return value from a separate thread. Meanwhile the main Servlet + container thread is released and allowed to process other requests. + Spring MVC invokes the Callable in a + separate thread with the help of a TaskExecutor + and when the Callable returns, the + request is dispatched back to the Servlet container to resume + processing with the value returned by the + Callable. + Here is an example controller method: + + +@RequestMapping(method=RequestMethod.POST) +public Callable<String> processUpload(final MultipartFile file) { + + return new Callable<String>() { + public Object call() throws Exception { + // ... + return "someView"; + } + }; +} + + A second option is for the controller to return an instance of + DeferredResult. In this case the return value + will also be produced from a separate thread. However, that thread is not + known to Spring MVC. For example the result may be produced in response + to some external event such as a JMS message, a scheduled task, etc. + Here is an example controller method: + + +@RequestMapping("/quotes") +@ResponseBody +public DeferredResult<String> quotes() { + DeferredResult<String> deferredResult = new DeferredResult<String>(); + // Save the deferredResult in in-memory queue ... + return deferredResult; +} + +// In some other thread... +deferredResult.setResult(data); + + + This may be difficult to understand without any knowledge of the + Servlet 3 async processing feature. It would certainly help to read up on it. + At a very minimum consider the following basic facts: + + + + A ServletRequest + can be put in asynchronous mode by calling + request.startAsync(). The main effect of doing so is + that the Servlet, as well as any Filters, can exit but the response + will remain open allowing some other thread to complete processing. + + + + The call to request.startAsync() returns an + AsyncContext, which can be used for + further control over async processing. For example it provides + the method dispatch, which can be called from an + application thread in order to "dispatch" the request back to + the Servlet container. An async dispatch is similar to a forward + except it is made from one (application) thread to another + (Servlet container) thread whereas a forward occurs synchronously + in the same (Servlet container) thread. + + + ServletRequest provides access + to the current DispatcherType, which + can be used to distinguish if a Servlet or + a Filter is processing on + the initial request processing thread and when it is processing in + an async dispatch. + + + + With the above in mind, the following is the sequence + of events for async request processing with a Callable: + (1) Controller returns a + Callable, (2) Spring MVC starts async processing + and submits the Callable + to a TaskExecutor + for processing in a separate thread, (3) the DispatcherServlet + and all Filter's exit the request processing thread but the response + remains open, (4) the Callable produces a result + and Spring MVC dispatches the request back to the Servlet container, + (5) the DispatcherServlet is invoked again and processing + resumes with the asynchronously produced result from the + Callable. The exact sequencing of (2), + (3), and (4) may vary depending on the speed of execution of the + concurrent threads. + + + The sequence of events for async request processing with a + DeferredResult is the same in principal except + it's up to the application to produce the asynchronous result from some thread: + (1) Controller returns a DeferredResult and saves it + in some in-memory queue or list where it can be accessed, + (2) Spring MVC starts async processing, (3) the DispatcherServlet + and all configured Filter's exit the request processing thread but the response + remains open, (4) the application sets the DeferredResult + from some thread and Spring MVC dispatches the request back to the Servlet container, + (5) the DispatcherServlet is invoked again and processing + resumes with the asynchronously produced result. + + + Explaining the motivation for async request processing, when or why to use it + is beyond the scope of this document. For further information you may wish to read + this blog post series. + + +
+ Async Request Processing and Exception Handling + + What happens if a Callable returned + from a controller method raises an Exception while being executed? + The effect is similar to what happens when any controller method raises + an exception. It is handled by a matching + @ExceptionHandler method in the same + controller or by one of the configured + HandlerExceptionResolver instances. + + + Under the covers, when a Callable + raises an Exception, Spring MVC still dispatches to the Servlet + container to resume processing. The only difference is that the + result of executing the Callable + is an Exception that must be processed + with the configured + HandlerExceptionResolver instances. + + + When using a DeferredResult, you have + a choice of calling its setErrorResult(Object) method + and provide an Exception or any other Object + you'd like to use as the result. If the result is an + Exception, it will be processed with a + matching @ExceptionHandler method in the + same controller or with any configured + HandlerExceptionResolver instance. +
+ +
+ Intercepting Async Requests + + An existing HandlerInterceptor can + implement AsyncHandlerInterceptor, which + provides one additional method afterConcurrentHandlingStarted. + It is invoked after async processing starts and when the initial + request processing thread is being exited. See the Javadoc of + AsyncHandlerInterceptor for more details + on that. + + Further options for async request lifecycle callbacks are + provided directly on DeferredResult, + which has the methods onTimeout(Runnable) and + onCompletion(Runnable). Those are called when the + async request is about to time out or has completed respectively. + The timeout event can be handled by setting the + DeferredResult to some value. + The completion callback however is final and the result can no + longer be set. + + Similar callbacks are also available with a + Callable. However, you will need to wrap + the Callable in an instance of + WebAsyncTask and then use that to register + the timeout and completion callbacks. Just like with + DeferredResult, the timeout event can be + handled and a value can be returned while the completion event is final. + + You can also register a + CallableProcessingInterceptor or a + DeferredResultProcessingInterceptor + globally through the MVC Java config or the MVC namespace. + Those interceptors provide a full set of callbacks and apply every + time a Callable or a + DeferredResult is used. + +
+ +
+ Async Request Configuration + + The MVC Java config and the MVC namespace provide options for + configuring async request processing. + WebMvcConfigurer has the method + configureAsyncSupport while <mvc:annotation-driven> + has an <async-support> sub-element. + + Those allow you to configure the default timeout value to use for + async requests, which if not set depends on the underlying Servlet + container (e.g. 10 seconds on Tomcat). You can also configure an + AsyncTaskExecutor to use for executing + Callable instances returned from + controller methods. It is highly recommended to configure this property + since by default Spring MVC uses + SimpleAsyncTaskExecutor. The MVC Java config + and the MVC namespace also allow you to register + CallableProcessingInterceptor and + DeferredResultProcessingInterceptor + instances. + + If you need to override the default timeout value for a + specific DeferredResult, you can do so by using + the appropriate class constructor. Similarly, for a + Callable, you can wrap it in a + WebAsyncTask and use the appropriate class + constructor to customize the timeout value. The class constructor of + WebAsyncTask also allows providing + an AsyncTaskExecutor. + +
+ +
+
Testing Controllers diff --git a/src/reference/docbook/new-in-3.2.xml b/src/reference/docbook/new-in-3.2.xml index 7f971419aec..6b5dcc47fda 100644 --- a/src/reference/docbook/new-in-3.2.xml +++ b/src/reference/docbook/new-in-3.2.xml @@ -37,10 +37,8 @@ - See - Introducing Servlet 3 Async Support (SpringSource team - blog). + See . +
@@ -258,5 +256,6 @@ linkend="testcontext-ctx-management-initializers">ApplicationContextInitializers +