Miles to go …

May 31, 2010

TOTD #139: Asynchronous Request Processing using Servlets 3.0 and Java EE 6

Filed under: General — arungupta @ 8:17 am

Server resources are always valuable and should be used conservatively. Consider a Servlet that has to wait for a JDBC connection to be available from the pool, receiving a JMS message or reading a resource from the file system. Waiting for a "long running" process to completely blocks the thread – waiting, sitting and doing nothing – not an optimal usage of your server resources. Servlets 3.0 introduces the ability to asynchronously process requests such that the control (or thread) is returned back to the container to perform other tasks while waiting for the long running process to complete. The request processing continues in the same thread after after the response from the long running process is returned or or may be dispatched to a new resource from within the long running process. A typical use case for long running process is a Chat Application.

The asynchronous behavior need to be explicitly enabled on a Servlet. This is achieved by adding "asyncSupported" attribute on @WebServlet as shown below:

@WebServlet(name="AsyncServlet", urlPatterns={"/AsyncServlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {

The asynchronous processing can then be started in a separate thread using "startAsync" method on the request. This method returns "AsyncContext" which represents the execution context of the asynchronous request. The asynchronous request can then be completed by calling AsyncContext.complete (explicit) or dispatching to another resource (implicit). The container completes the invocation of asynchronous request in the later case.

Lets say the long running process is implemented as:

class MyAsyncService implements Runnable {
AsyncContext ac;
public MyAsyncService(AsyncContext ac) {
this.ac = ac;
}
@Override
public void run() {
System.out.println("Some long running process in \"MyAsyncService\"");
}
}

Note this is running in a separate thread. This service can be invoked from the original servlet as:

AsycnContext ac = request.startAsync();

The service may also be started as:

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
executor.execute(new MyAsyncService(ac));

The ScheduledThreadPoolExecutor usage is recommended as it makes the thread management easier.

A listener can be associated with the AsyncContext to get notified of when the async request is complete, timed out, or resulted in an error. This can be achieved as:

ac.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("onComplete.");
}
. . .
});

A more complete code for the above fragment is given below. The "onComplete" method is used to clean up resources created during async processing.

The asynchronous request processing can be either completed in "run" method of "MyAsyncService" by invoking "AsycnContext.complete" as shown below:

@Override
public void run() {
System.out.println("Some long running process in \"MyAsyncService\"");
ac.complete();
}

or dispatched to a different resource as:

@Override
public void run() {
System.out.println("Some long running process in \"MyAsyncService\"");
ac.dispatch("/response.jsp");
}

Lets take a look at our complete code:

@WebServlet(name="AsyncServlet", urlPatterns={"/AsyncServlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
try {
System.out.println("Starting doGet");
AsyncContext ac = request.startAsync();
ac.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("onComplete");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("onTimeout");
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println("onError");
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("onStartAsync");
}
});
System.out.println("Do some stuff in doGet ...");
// Start another service
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
executor.execute(new MyAsyncService(ac));
System.out.println("Some more stuff in doGet ...");
} finally {
}
}
class MyAsyncService implements Runnable {
AsyncContext ac;
public MyAsyncService(AsyncContext ac) {
this.ac = ac;
System.out.println("Dispatched to " + "\"MyAsyncService\"");
}
@Override
public void run() {
System.out.println("Some long running process in \"MyAsyncService\"");
ac.complete();
}
}
}

Invoking the GET operation of this Servlet shows the following sequence of statements in GlassFish server log:

INFO: Starting doGet
INFO: Dispatched to "MyAsyncService"
INFO: Doing some stuff in doGet ...
INFO: Some more in doGet ...
INFO: Some long running process in "MyAsyncService"
INFO: onComplete

The first statement marks the beginning of "doGet", the second statement indicates that the request is dispatched to the long running
"MyAsyncService", the third and fourth statement indicates that the response returned back to "doGet" for normal request processing. Finally fifth and sixth statement shows that now the asynchronous request is getting processed and is completed.

Clean and simple!

A request may be dispatched from Asynchronous servlet to Synchronous but other way around is illegal. The section 2.3.3.3 in the Servlets 3.0 specification provides an introduction to the Asynchronous Processing in Servlets 3.0.

The asynchronous behavior is available in the Servlet Filter as well.

The complete code used in this blog can be downloaded here. A fully working sample of a Chat Application using Servlets 3.0 can be seen here.

Technorati: totd glassfish v3 servlets javaee asynchronous comet

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • StumbleUpon
  • Technorati
  • Twitter
  • Slashdot
Related posts:
  1. TOTD #137: Asynchronous EJB, a light-weight JMS solution – Feature-rich Java EE 6
  2. TOTD #132: Servlets 3.0 in Embedded GlassFish Reloaded – lightweight Java EE 6
  3. TOTD #136: Default Error Page using Servlets 3.0 – Improved productivity using Java EE 6
  4. TOTD #120: Deployment Descriptor-free Java EE 6 application using JSF 2.0 + EJB 3.1 + Servlets 3.0
  5. TOTD #158: Java EE 7 JSRs: JPA 2.1, JAX-RS 2.0, Servlets 3.1, EL 3.0, JMS 2.0, JSF 2.2, CDI 1.1, Bean Validation 1.1

5 Comments »

  1. What’s the purpose with the line "request.getServletContext().setAttribute("asyncContext", ac);" ?

    To me, it looks like a bad idea to store this within the servlet-context?

    Comment by erik — June 1, 2010 @ 12:22 am

  2. Erik,

    That was a typo from something I was trying for experimentation purpose, now cleaned up.

    Comment by Arun Gupta — June 1, 2010 @ 12:52 am

  3. Why use ScheduledThreadPoolExecutor instead of Executors.newFixedThreadPool?

    Comment by f4kingit — August 1, 2010 @ 11:35 pm

  4. Hi arun

    Thanks for you article,when i run this project in netbeans it just shows "Hello World" which comes from index.jsp.but it would be great if you can show us a complete life cycle of which makes request from html/jsp/jsf page to AsyncServlet, servlet then process those and render.Like glassfish chat application,but this chat application is not well documented so hard to understand and kind of complex javascript layer.

    just a simple example :)

    Comment by jar — October 19, 2010 @ 10:22 pm

  5. jar,

    Thanks for the suggestion, added to my TODO list :-)

    Comment by Arun Gupta — October 21, 2010 @ 9:13 am

RSS feed for comments on this post. TrackBack URL

Leave a comment

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.
Powered by WordPress