X

Asynchronous Support in Servlet 3.0

by Rajiv Mordani

One of the significant enhancements made in
JSR 315: Java Servlet 3.0, is support for asynchronous processing.
With this support, a servlet no longer has to wait for a response from a resource such as a database before its thread can continue
processing, that is, the thread is not blocked. Previous to Servlet 3.0, asynchronous support in the Java EE web container was
provided in a proprietary way — either through APIs built on top of the Servlet 2.5 API or through non-Servlet APIs or
implementations.

In modern web applications there are times when you need to asynchronously handle some part of the request, so that the web container
can continue serving other requests in a seamless fashion. One example is a chat room application. In this type of application you
want to enable long-lived client connections with a servlet. You don't want a server thread to be blocked for a long period of time
serving a request from a single client. You want the servlet to process a request from the client and then free up the server thread
as quickly as possible for other work. In other words, you want the request to be processed and a response generated asynchronously.

With the upcoming release of the Servlet 3.0 specification, standard asynchronous support has been added to the Servlet API.
This Tech Tip introduces the asynchronous support that has been added to the Servlet API in Java Servlet 3.0 technology.
It also includes a sample application that demonstrates those features.

Asynchronous Support Features

The primary features of the asynchronous support in Servlet 3.0 are as follows.

Annotation Attributes

Servlet 3.0 introduces the use of annotations as an alternative to deployment descriptors
for servlet-related configuration in a web application. Two of these annotations are @WebServlet, which defines a servlet,
and @WebFilter, which defines a servlet filter. Both of these annotations include an attribute, asyncSupported.
Setting the asyncSupported attribute to true, declares that the servlet or servlet filter supports asynchronous
processing. For example, the following annotation defines a servlet in a web application and declares that the servlet supports
asynchronous processing:



   @WebServlet(url="/foo" asyncSupported=true)




The asyncSupported attribute is needed to differentiate code written for synchronous processing from that written for use in
an asynchronous context. In fact, for an application to use the asynchronous feature, the entire request processing chain must have
the have this attribute set either through the annotation or in its deployment descriptor. An IllegalStateException will be thrown
if an application tries to start an asynchronous operation and there is a servlet or servlet filter in the request processing chain
that does not support asynchronous processing.

Servlet Request Methods

The support for asynchronous processing also includes new ServletRequest methods, such as
startAsync(servletRequest, servletResponse), startAsync(), and getAsyncContext(). After you set the
asyncSupported attribute in the request processing chain to support asynchronous processing, you call either the
startAsync(servletRequest, servletResponse) or startAsync() method to make an asynchronous request.
Here, for example, is a call that makes an asynchronous request:



   AsyncContext aCtx = req.startAsync(req, res);




The difference between the two startAsync method signatures is that the startAsync() method implicitly uses
the original request and response, while the startAsync(servletRequest, servletResponse) method uses the request and response
objects passed in the method call. The request and response passed in the call can be wrapped by filters or other servlets earlier in the
request processing chain. Notice that the startAsync method returns an AsyncContext object —
see AsyncContext Class for more details. The AsyncContext object is initialized appropriately
with the request and response objects depending on the method used. You must exercise caution when wrapping the response and calling the
no arguments signature of the startAsync() method. You could lose the data if any of data is written to the wrapped
response and not flushed to the underlying response stream.

There are a few more methods in the ServletRequest class that are part of the support for asynchronous processing.
These include isAsyncSupported() and isAsyncStarted() You can use these convenience methods in an application
to determine if asynchronous operations are supported or started on a request.


AsyncContext Class

The AsyncContext class is a new class in Servlet 3.0 that provides the execution context for an asynchronous operation.
The class provides a variety of methods that you can use to get access to the underlying request and response objects.
For example, you can use the AsyncContext.dispatch(), AsyncContext.dispatch(path),
or AsyncContext.dispatch(servletContext, path) method to dispatch the request to the container. Using any of the
dispatch methods, enables the processing to return to the container after the asynchronous operation that was started on the ServletRequest is
completed — for instance, after waiting for a call to a web service to return. These methods dispatch the request back to the container
so you can use frameworks such as JavaServer Pages (JSP) to generate the response.

The dispatch() method, that is, the method with no arguments, forwards the request back to the original URL.
Also, if a call to AsyncContext.dispatch or RequestDispatcher.forward occurs after an asynchronous context
has been initialized, the dispatch() method forwards the request to the path for the AsyncContext
or RequestDispatcher-related requests.

The dispatch(path) method forwards the request to the path relative to the context of the request.

The dispatch(ServletContext, path) method forwards the request to the path relative to the specified context.
For example, here is a call that forwards the request back to the container so that the JSP framework can generate the response.



   ctx.dispatch("/render.jsp");




Another AsyncContext method, complete(), completes the asynchronous operation that was started on the request that
was used to initialize this AsyncContext object. It closes the response that was used to initialize this AsyncContext
object. You can call this method, for example, when the response generated by the asynchronous operation is complete.
The container can also implicitly call this method if the application dispatches the request back to the container using the forward
method with no subsequent startAsync call on the request.

Asynchronous Listener Class

Servlet 3.0 also adds a new listener class for asynchronous processing, AsyncListener. You can use this class in an
application to get notified when asynchronous processing is completed, if a timeout has occurred, an error has occurred, or a subsequent call
to startAsync has occurred. The following code snippet creates a new AsyncListener object and uses it to
get notified when an asynchronous processing operation is completed .



   AsyncContext ac = req.startAsync();
req.addAsyncListener(new AsyncListener() {
public void onComplete(AsyncEvent event) throws IOException {
...
}
...
}




Notice that the parameter passed to the onComplete method is an AsyncEvent object. The
AsyncEvent class is another new class provided as part of the Servlet 3.0 support for asynchronous processing.
It represents an event that gets fired when the asynchronous operation initiated on a ServletRequest
has completed, timed out, or produced an error.

A Simple Application That Uses the Asynchronous Support in Servlet 3.0

Now that you've seen the key features in the Servlet 3.0 support for asynchronous processing, let's look at a more complete
example. The following shows the code for a simple web application. In the application, a servlet makes an asynchronous request
to a web service, waits for the call to return, and then dispatches the request back to the container to render the result
using JSP. Note that the complete code for servlet is not shown.



   @WebServlet("/foo" asyncSupported=true)
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) {
...
AsyncContext aCtx = request.startAsync(req, res);
ScheduledThreadPoolExecutor executor = new ThreadPoolExecutor(10);
executor.execute(new AsyncWebService(aCtx));
}
}
public class AsyncWebService implements Runnable {
AsyncContext ctx;
public AsyncWebService(AsyncContext ctx) {
this.ctx = ctx;
}
public void run() {
// Invoke web service and save result in request attribute
// Dispatch the request to render the result to a JSP.
ctx.dispatch("/render.jsp");
}
}




Sample Application

One of the sample applications provided with the Java EE 6 SDK
is a chat application that uses the asynchronous support in Servlet 3.0.

To run the application, do the following:


  1. If you haven't already done so, download the Java EE 6 SDK. Also be sure to have an installed version of the
    Java Platform Standard Edition (Java SE) 6 SDK.
  2. Download the sample application,
    async-request-war.war
  3. Start the GlassFish v3 application server that is packaged with the Java EE 6 SDK by entering the
    following command:

       <javaee_home>/bin/asadmin start-domain





    where <javaee_home> is where you installed the Java EE 6 SDK.
  4. Deploy the sample application by copying it to the <javaee_home>/domains/domain1/autodeploy directory.
  5. Execute the application by opening two browser windows. For simplicity, let's call them say Browser A and Browser B.
    • Point both Browser A and Browser B to the URL http://localhost:8080/async_request_war. In response, you should see
      a chat user interface (UI) in each browser window. The UI presents a text area to view chat entries, a text field to
      enter a user ID, and a Login button.
    • In Browser A, enter a user name, say "userA", in the text field and click the Login button. You should see
      the following message appear in the text area:
            System Message:
      userA has joined.

      You should also see a new text area for posting messages to the chat and a button labeled Post Message.


    • Login as "userB" in Browser B. You should see the following message appear in both browser windows:



             System Message:
      userB has joined.






      The text area for posting messages and the Post Message button should appear in Browser B.

      Now, you're ready to chat.

    • In Browser A, enter "Hello" in the text box and click the Post Message button.
      You should see the following message in both Browser A and Browser B:

             userA:
      Hello

    • In Browser B, enter "Hello" in the text box and click the Post Message button.
      You should see the following message in both Browser A and Browser B:

             userB:
      Hello

You can view the source code for the application in the <javaee_home>/samples/javaee6/web/servlet/async-request-war/src
directory.

Further Reading

For more information, see the following resources:


About the Author

Rajiv Mordani is a senior staff engineer at Sun Microsystems. He was part of the original J2EE platform team and has worked on
a wide variety of server-side XML and web technologies, including Jakarta Tomcat and most recently Java Servlet 3.0.
Rajiv is currently the specification lead for Servlet 3.0. Read his blog.

Join the discussion

Comments ( 4 )
  • Eric Ma Monday, December 14, 2009

    Did HttpServletRequest.setAsyncTimeout make into the final spec? I ran the async_request_war sample app in GlassFish v3 and got the following:

    [#|2009-12-14T23:06:20.237-0500|WARNING|glassfishv3.0|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=29;_ThreadName=http-thread-pool-8080-(3);|StandardWrapperValve[web.servlet.async_request_war.AjaxCometServlet]: PWC1406: Servlet.service() for servlet web.servlet.async_request_war.AjaxCometServlet threw exception

    java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRequest.setAsyncTimeout(J)V

    any ideas?


  • Rajiv Mordani Friday, December 18, 2009

    @Eric the APIs have been updated, please download the latest sample as well and that should work. Sorry about the confusion.


  • Eric Ma Friday, December 18, 2009

    There is no web.xml in the war?


  • dizi izle Saturday, January 23, 2010

    thank you for your article...that is a nice one...i often visit this blog and i enjoy reading all posts...


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha