Update: One should not use response in AsyncListener#onComplete. Only print debug in this example.

Servlet 3.1 (JSR 340) is almost ready for the release. One of the new features is the support for non-blocking IO. ReadListener and WriteListener are introduced to allow non-blocking processing in Servlet. Non-blocking IO can only be used in async (defined in Servlet 3.0) or the upgrade mode. We can set the async in a servlet with @WebServlet annotation. In this blog, we will illustrate the use of the new API with an example having non-blocking read and then non-blocking write. See the comments in the code. // enable async in the servlet @WebServlet(urlPatterns="/test", asyncSupported=true) public class TestServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { // start async AsyncContext ac = req.startAsync(); // set up async listener ac.addListener(new AsyncListener() { public void onComplete(AsyncEvent event) throws IOException { System.out.println("Complete"); } public void onError(AsyncEvent event) { System.out.println(event.getThrowable()); } public void onStartAsync(AsyncEvent event) { } public void onTimeout(AsyncEvent event) { System.out.println("my asyncListener.onTimeout"); } }); // set up ReadListener to read data for processing ServletInputStream input = req.getInputStream(); ReadListener readListener = new ReadListenerImpl(input, res, ac); input.setReadListener(readListener); } } All the read operation has been delegated to ReadListenerImpl. The data will be read in non-blocking mode. After ServletInputStream#setReadListener is called, ReadListener#onDataAvailable will be invoked immediately if there is data to read. Otherwise, it will be invoked once the data is ready. The implementation is as follows: class ReadListenerImpl implements ReadListener { private ServletInputStream input = null; private HttpServletResponse res = null; private AsyncContext ac = null; // store the processed data to be sent back to client later private Queue queue = new LinkedBlockingQueue(); ReadListenerImpl(ServletInputStream in, HttpServletResponse r, AsyncContext c) { input = in; res = r; ac = c; } public void onDataAvailable() throws IOException { StringBuilder sb = new StringBuilder(); int len = -1; byte b[] = new byte[1024]; // We need to check input#isReady before reading data. // The ReadListener will be invoked again when // the input#isReady is changed from false to true while (input.isReady() && (len = input.read(b)) != -1) { String data = new String(b, 0, len); sb.append(data); } queue.add(sb.toString()); } public void onAllDataRead() throws IOException { // now all data are read, set up a WriteListener to write ServletOutputStream output = res.getOutputStream(); WriteListener writeListener = new WriteListenerImpl(output, queue, ac); output.setWriteListener(writeListener); } public void onError(final Throwable t) { ac.complete(); t.printStackTrace(); } } When all the data is read, ReadListenerImpl#onAllDataRead sets up a WriteListenerImpl for writing data in a non-blocking mode. class WriteListenerImpl implements WriteListener { private ServletOutputStream output = null; private Queue queue = null; private AsyncContext ac = null; WriteListenerImpl(ServletOutputStream sos, Queue q, AsyncContext c) { output = sos; queue = q; ac = c; } public void onWritePossible() throws IOException { // write while there is data and is ready to write while (queue.peek() != null && output.isReady()) { String data = queue.poll(); output.print(data); } // complete the async process when there is no more data to write if (queue.peek() == null) { ac.complete(); } } public void onError(final Throwable t) { ac.complete(); t.printStackTrace(); } }