diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 6a57ec71acd9824c4273026d203964373be58c4a..8cc3535eb6cdacfac6b5a964dfdefd7376d4ffaa 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -2054,12 +2054,12 @@ customized through `jsonpParameterNames` property. === 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 +`java.util.concurrent.Callable` and produce the return value from a Spring MVC managed thread. +Meanwhile the main Servlet container thread is exited and 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: +Servlet container to resume processing using the value returned by the `Callable`. Here +is an example of such a controller method: [source,java,indent=0] [subs="verbatim,quotes"] @@ -2077,11 +2077,11 @@ is an example controller method: } ---- -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: +Another option is for the controller method to return an instance of `DeferredResult`. In this +case the return value will also be produced from any thread, i.e. one that +is not managed by Spring MVC. For example the result may be produced in response to some +external event such as a JMS message, a scheduled task, and so on. Here is an example +of such a controller method: [source,java,indent=0] [subs="verbatim,quotes"] @@ -2090,7 +2090,7 @@ controller method: @ResponseBody public DeferredResult quotes() { DeferredResult deferredResult = new DeferredResult(); - // Save the deferredResult in in-memory queue ... + // Save the deferredResult somewhere.. return deferredResult; } @@ -2098,102 +2098,187 @@ controller method: 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: +This may be difficult to understand without any knowledge of the Servlet 3.0 +asynchronous request processing features. It would certainly help to read up +on that. Here are a few basic facts about the underlying mechanism: * 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 + the response will remain open to allow processing to complete later. +* The call to `request.startAsync()` returns `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. + that is similar to a forward from the Servlet API except it allows an + application to resume request processing on a Servlet container thread. +* The `ServletRequest` provides access to the current `DispatcherType` that can + be used to distinguish between processing the initial request, an async + dispatch, a forward, and other dispatcher types. 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 and when or why to use it are -beyond the scope of this document. For further information you may wish to read +processing with a `Callable`: + +* Controller returns a `Callable`. +* Spring MVC starts asynchronous processing and submits the `Callable` to + a `TaskExecutor` for processing in a separate thread. +* The `DispatcherServlet` and all Filter's exit the Servlet container thread + but the response remains open. +* The `Callable` produces a result and Spring MVC dispatches the request back + to the Servlet container to resume processing. +* The `DispatcherServlet` is invoked again and processing resumes with the + asynchronously produced result from the `Callable`. + +The sequence for `DeferredResult` is very similar except it's up to the +application to produce the asynchronous result from any thread: + +* Controller returns a `DeferredResult` and saves it in some in-memory + queue or list where it can be accessed. +* Spring MVC starts async processing. +* The `DispatcherServlet` and all configured Filter's exit the request + processing thread but the response remains open. +* The application sets the `DeferredResult` from some thread and Spring MVC + dispatches the request back to the Servlet container. +* The `DispatcherServlet` is invoked again and processing resumes with the + asynchronously produced result. + +For further background on the motivation for async request processing and +when or why to use it please read https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[this blog post series]. [[mvc-ann-async-exceptions]] ==== Exception Handling for Async Requests -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. - -[NOTE] -==== -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. +What happens if a `Callable` returned from a controller method raises an +Exception while being executed? The short answer is the same as what happens +when a controller method raises an exception. It goes through the regular +exception handling mechanism. The longer explanation is that when a `Callable` +raises an Exception Spring MVC dispatches to the Servlet container with +the `Exception` as the result and that leads to resume request processing +with the `Exception` instead of a controller method return value. +When using a `DeferredResult` you have a choice whether to call +`setResult` or `setErrorResult` with an `Exception` instance. [[mvc-ann-async-interception]] ==== 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 `AsyncHandlerInterceptor` javadocs for more details on that. +A `HandlerInterceptor` can also implement `AsyncHandlerInterceptor` in order +to implement the `afterConcurrentHandlingStarted` callback, which is called +instead of `postHandle` and `afterCompletion` when asynchronous processing +starts. + +A `HandlerInterceptor` can also register a `CallableProcessingInterceptor` +or a `DeferredResultProcessingInterceptor` in order to integrate more +deeply with the lifecycle of an asynchronous request and for example +handle a timeout event. See the Javadoc of `AsyncHandlerInterceptor` +for more details. + +The `DeferredResult` type also provides methods such as `onTimeout(Runnable)` +and `onCompletion(Runnable)`. See the Javadoc of `DeferredResult` for more +details. + +When using a `Callable` you can wrap it with an instance of `WebAsyncTask` +which also provides registration methods for timeout and completion. + +[[mvc-ann-async-http-streaming]] +==== HTTP Streaming + +A controller method can use `DeferredResult` and `Callable` to produce its +return value asynchronously and that can be used to implement techniques such as +http://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[long polling] +where the server can push an event to the client as soon as possible. -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. +What if you wanted to push multiple events on a single HTTP response? +This is a technique related to "Long Polling" that is known as "HTTP Streaming". +Spring MVC makes this possible through the `ResponseBodyEmitter` return value +type which can be used to send multiple Objects, instead of one as is normally +the case with `@ResponseBody`, where each Object sent is written to the +response with an `HttpMessageConverter`. + +Here is an example of that: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RequestMapping("/events") + public ResponseBodyEmitter handle() { + ResponseBodyEmitter emitter = new ResponseBodyEmitter(); + // Save the emitter somewhere.. + return emitter; + } + + // In some other thread + emitter.send("Hello once"); + + // and again later on + emitter.send("Hello again"); + + // and done at some point + emitter.complete(); +---- -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. +Note that `ResponseBodyEmitter` can also be used as the body in a +`ResponseEntity` in order to customize the status and headers of +the response. -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. +[[mvc-ann-async-sse]] +==== Server-Sent Events + +`SseEmitter` is a sub-class of `ResponseBodyEmitter` providing support for +http://www.w3.org/TR/eventsource/[Server-Sent Events]. +Server-sent events is a just another variation on the same "HTTP Streaming" +technique except events pushed from the server are formatted according to +the W3C Servet-Sent Events specification. + +Server-Sent Events can be used for their intended purpose, that is to push +events from the server to clients. It is quite easy to do in Spring MVC and +requires simply returning a value of type `SseEmitter`. + +Note however that Internet Explorer does not support Server-Sent Events and +that for more advanced web application messaging scenarios such as online games, +collaboration, financial applicatinos, and others it's better to consider +Spring's WebSocket support that includes SockJS-style WebSocket emulation +falling back to a very wide range of browsers (including Internet Explorer) +and also higher-level messaging patterns for interacting with clients through +a publish-subscribe model within a more messaging-centric architecture. +For further background on this see +http://blog.pivotal.io/pivotal/products/websocket-architecture-in-spring-4-0[the following blog post]. + +[[mvc-ann-async-output-stream]] +==== HTTP Streaming Directly To The OutputStream + +`ResponseBodyEmitter` allows sending events by writing Objects to the +response through an `HttpMessageConverter`. This is probably the most common +case, for example when writing JSON data. However sometimes it is useful to +bypass message conversion and write directly to the response `OutputStream` +for example for a file download. This can be done with the help of the +`StreamingResponseBody` return value type. + +Here is an example of that: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RequestMapping("/download") + public StreamingResponseBody handle() { + return new StreamingResponseBody() { + @Override + public void writeTo(OutputStream outputStream) throws IOException { + // write... + } + }; + } +---- + +Note that `StreamingResponseBody` can also be used as the body in a +`ResponseEntity` in order to customize the status and headers of +the response. [[mvc-ann-async-configuration]] -==== Configuration for Async Request Processing +==== Configuring Asynchronous Request Processing [[mvc-ann-async-configuration-servlet3]] -===== Servlet 3 Async Config -To use Servlet 3 async request processing, you need to update `web.xml` to version 3.0: +===== Servlet Container Configuration +For applications configured with a `web.xml` be sure to update to version 3.0: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -2209,22 +2294,15 @@ To use Servlet 3 async request processing, you need to update `web.xml` to versi ---- -The `DispatcherServlet` and any `Filter` configuration need to have the -`true` sub-element. Additionally, any `Filter` that -also needs to get involved in async dispatches should also be configured to support the -ASYNC dispatcher type. Note that it is safe to enable the ASYNC dispatcher type for all -filters provided with the Spring Framework since they will not get involved in async -dispatches unless needed. +Asynchronous support must be enabled on the `DispatcherServlet` through the +`true` web.xml sub-element. Additionally +any `Filter` that participates in asyncrequest processing must be configured +to support the ASYNC dispatcher type. It should be safe to enable the ASYNC +dispatcher type for all filters provided with the Spring Framework since they +usually extend `OncePerRequestFilter` and that has runtime checks for whether +the filter needs to be involved in async dispatches or not. -[WARNING] -==== -Note that for some Filters it is absolutely critical to ensure they are mapped to -be invoked during asynchronous dispatches. For example if a filter such as the -`OpenEntityManagerInViewFilter` is responsible for releasing database connection -resources and must be invoked at the end of an async request. - -Below is an example of a propertly configured filter: -==== +Below is some example web.xml configuration: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -2253,18 +2331,20 @@ Below is an example of a propertly configured filter: ---- -If using Servlet 3, Java based configuration, e.g. via `WebApplicationInitializer`, +If using Servlet 3, Java based configuration for example via `WebApplicationInitializer`, you'll also need to set the "asyncSupported" flag as well as the ASYNC dispatcher type just like with `web.xml`. To simplify all this configuration, consider extending `AbstractDispatcherServletInitializer` or -`AbstractAnnotationConfigDispatcherServletInitializer`, which automatically set those -options and make it very easy to register `Filter` instances. +`AbstractAnnotationConfigDispatcherServletInitializer` which automatically +set those options and make it very easy to register `Filter` instances. [[mvc-ann-async-configuration-spring-mvc]] -===== Spring MVC Async Config -The MVC Java config and the MVC namespace both provide options for configuring async -request processing. `WebMvcConfigurer` has the method `configureAsyncSupport` while - has an sub-element. +===== Spring MVC Configuration + +The MVC Java config and the MVC namespace provide options for configuring +asynchronous request processing. `WebMvcConfigurer` has the method +`configureAsyncSupport` while `` has an +`` 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