Sunday, December 22, 2013

Asynchronous Calls with Servlet 3.0

Background

With the advent of Rich Internet Application (RIA) and data-hungry HTML5, asynchronous calls are becoming more and more inevitable at every level of web application, including servlet layer. In typical “thread-per-request” model, web container assigns one thread, from pool, for each http request and runs the doXXX method of the servlet. But in cases where the doXXX methods need long time to complete, the thread is engaged with the underlying request. This creates bottleneck and limits the number of requests which can be served by web container. Although there were proprietary implementation to achieve asynchronization (Comet apps as an example), none of them provide true sense of it. I’ve seen many implementations where developers trigger a new thread inside doXXX to accomplish the time-taking tasks, but the biggest fault of such implementations is that the response is committed back to client as soon as the doXXX method ends and then you need to poll the server to get the result of the original request.
One of the important features of Servlet 3.0 is the provision for Asynchronous calls. The center of this is the AsyncContext class, which encapsulates the request and response objects and provides methods to commit/dispatch the response at later point of time. The noticeable difference here is that, on the completion of doXXX method, the thread is simply returned to pool but the response is not committed. The request and response can be retrieved from AsyncContext object later on and then response be committed from there or dispatched to another resource (servlet or jsp). Let’s consider an exemplary code snippet to understand it better:

@WebServlet(value = "/sample", asyncSupported = true)
public class SampleAsyncServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

        AsyncContext asyncContext = req.startAsync(req, res);
        asyncContext.getResponse().getWriter().println("Starting new asynchronous request…");
        asyncContext.start(new LongRunner(asyncContext));
    }//This doesn’t commit response.


    private class LongRunner implements Runnable {

        AsyncContext asyncContext; 
        public LongRunner(AsyncContext asyncContext) {
            this.asyncContext = asyncContext;
        }
 
        @Override
        public void run() {
            //Do some long process here……
            try {
                asyncContext.getResponse().getWriter().printf("Output of long process……”);
            } catch (IOException e) {
                //Handle exception
            } finally {
                asyncContext.complete(); //This commits the response back to client
            }
        }
    }
}

In the above code, inside doGet method, a new AsyncContext object is created and a new thread is started using its start method. This start the execution in a new thread while the original thread associated with doGet method is returned back to the pool. The response is not yes committed to client. In the new thread, once the task is done, the response is sent back to client using complete method of AsyncContext.  

So, theoretically, the steps are as follows:
1.       Client sends the request to web container
2.       Web container gets a thread from the pool and runs the doXXX method of the servlet
3.       With request.startAsync() method, a new AsynContext object is created which encapsulates the req/resp.
4.       You can either start a new thread and pass it to start() method of AsyncContext object to start long & time consuming process or store AsyncContext object at appropriate level and retrieve later at valid trigger.
5.       The completion of method doXXX, simply releases the thread back to pool, but response is not sent back to client.
6.       Req/Resp objects are retrieved from AsyncContext object and worked on.
Finally response is committed by calling complete() or dispatch() method to dispatch to other resource (servlet/jsp).

How to Enable Asynchronous?

By default it’s turned off. It can be enabled either using annotations or in web.xml
 
@WebServlet (urlPatterns = {"/sample"}, asyncSupported=true)
   public class SampleServlet extends HttpServlet {

OR

    <servlet>
        <servlet-name>sample</servlet-name>
        <servlet-class>com.sample.servlet.SampleServlet</servlet-class>
        <async-supported>true</async-supported>
</servlet>
Further Reading

Conclusion
Servlet 3.0 added the provision for asynchronous calls, which provides great flexibility and avoids the need for proprietary implementation to achieve the same in web applications.

No comments: