Close

Servlet - Asynchronous Processing

[Last Updated: Dec 11, 2016]

Sometimes a filter or servlet is unable to complete the processing of a request quickly. They may need to wait for an JDBC connection to be available, for a response from a remote service, for a JMS message, or for an application event or for any kind of long running task.

The asynchronous processing of requests is introduced to allow the request thread to exit and return to the container's thread pool to handle other requests. The current long processing request is continued in a background thread.



Enabling asynchronous support

The @WebServlet and @WebFilter annotations have an attribute - asyncSupported that is a boolean with a default value of false. When asyncSupported is set to true the application can start asynchronous processing in a separate thread by calling HttpServletRequest#startAsync(). Calling this method will delay the response until AsyncContext.complete() is called on the returned AsyncContext, or the asynchronous operation has timed out.

@WebServlet(name = "myServlet", urlPatterns = {"/async-test"},
                    asyncSupported = true )
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse resp) throws ServletException, IOException {

        PrintWriter writer = resp.getWriter();
        AsyncContext asyncContext = req.startAsync(); 

        asyncContext.start(new Runnable() {
            @Override
            public void run () {
                String msg = task();
                writer.println(msg);
                 asyncContext.complete(); 
            }
        });
    }

    private String task() {
        long start = System.currentTimeMillis();
        try {//simulating a blocking task here
            int i = ThreadLocalRandom.current()
                                     .nextInt(1, 5);
            Thread.sleep(i*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "time to complete long task "+(System.currentTimeMillis()-start);
    }
}



What is AsyncContext?

The interface javax.servlet.AsyncContext represents the execution context for an asynchronous task.

Important methods are:

  • start(Runnable runnable), starts the async process by spawning a new thread.
  • Complete(): Commits the response.
  • setTimeout(...): Sets the timeout in milli-sec for this AsyncContext. The default timeout dependentds on the container implementation e.g. Tomcat's default timeout is 10 sec.
  • addListener(AsyncListener listener, ...): Adds the listener that will be notified in the event that an asynchronous operation has completed, timed out, or resulted in an error.
  • <T extends AsyncListener> T createListener(Class<T> clazz): Instantiates the given AsyncListener class. There must be a default constructor. This method supports resource injection if the given clazz represents a Managed Bean.
  • dispatch(...): Dispatches the request and response objects of this AsyncContext to the given path. Dispatching from a servlet that has asyncSupported=true to one where asyncSupported is set to false is allowed. In this case, the response will be committed when the service method of the servlet that does not support async is exited



Creating AsyncListener

public class MyAsyncListener implements AsyncListener {
    Logger logger = Logger.getLogger(MyAsyncListener.class.getName());

    @Override
    public void onComplete (AsyncEvent event) throws IOException {
       logger.info("async process completed ");
    }

    @Override
    public void onTimeout (AsyncEvent event) throws IOException {
        logger.info("async process timeout");
        writeToResponse(event, "async process time out "+System.currentTimeMillis());
    }

    @Override
    public void onError (AsyncEvent event) throws IOException {
        logger.info("async error");
        writeToResponse(event, event.getThrowable().getMessage());
    }

    @Override
    public void onStartAsync (AsyncEvent event) throws IOException {
        logger.info("async process started");
    }

    private void writeToResponse(AsyncEvent event, String message) throws IOException {
        ServletResponse response = event.getAsyncContext().getResponse();
        PrintWriter out = response.getWriter();
        out.write(message);
    }
}

  AsyncContext asyncContext = req.startAsync();
  asyncContext.addListener(new MyAsyncListener());

Example Project

Dependencies and Technologies Used:

  • Java Servlet API 3.0.1
  • JDK 1.8
  • Maven 3.0.4

Servlet Sync Example Select All Download
  • web-servlet-async
    • src
      • main
        • java
          • com.logicbig.servlet
            • MyServlet.java

    See Also