After my blog on the Servlet 3.0 Public Review, I have been working on an implementation of the Servlet-3.0 asynchronous servlets with some the fixes/extensions I suggested in my previous blog and from some continuing discussion within the EG. Specifically:
- A new ASYNC DispatcherType for redispatched asynchronous requests
- The isAsyncStarted() method is false when a request is redispatched.
- An IllegalStateException is thrown is startAsync() or startAsync(request.response) are called if getReader() or getOutputStream() have been called. This restricts asynchronous handler to the simpler cases.
- If asynchronous mode was started with startAsync(request,response), then it is an IllegalStateException to use any of the forward(…) methods on AsyncContext. This avoids the complication of redispatching wrappers, but allows wrappers to be used by asynchronous handlers.
- The forward(path) and forward(context,path) methods have not been implemented.
- The ChatServlet demo provides a simple chat room in a single class, using startAsync() and forward().
- The Cometd implementation and AsyncRestServlet demo have also been updated to use startAsync() and forward().
- The AsyncProxyServlet used the startAsync(request,response) and complete() style, together with the jetty asynchronous client.
- The QoSFilter has been updated to use startAsync() and forward()
- The GzipFilter has been updated to handle down stream code calling either startAsync() or startAsync(request,response).
- The Dump servlet has a number of async test parameters used in automated and manual tests.
This implementation has been vastely more succesful than my abandoned attempt to implement the pure PR proposal. It shows that with a few reasonable extensions and restrictions, that a capable and usable asynchronous API is possible (albiet a little more complex than truly neccessary). Highlights of this implementation include:
- It supports both wrapper-less startAsync() to forward() style
- It supports wrapper preserving startAsync(req,res) to complete() style.
- The GzipFilter can be applied to the ChatServlet (or similar apps) and will successfully gzip responses above a configured size, even on redispatched requests. You can test it with short chat sentances being sent in the clear and longer ones being gzipped.
- The GzipFilter can be applied to the AsyncProxyServlet, so that wrappers are preserved and responses obtained from the proxied server are gzipped as they are proxied.
- QoSFilter can be applied to normal code or asynchronous code.
- The Dump servlet async tests word even when accessed via RequestDispatcher forwards calls.
While more testing is needed, this implementation demonstrates that significant asynchronous behaviour can be implemented without the complexities of redispatching wrapped requests or the forward(path) methods. I believe it represents a reasonable compromise for 3.0. If additional features are required, surely they can be added in 3.1 after we have gained experience with a simpler subset from 3.0?
To criticize my own work, I would say that the use of ISE to restrict behaviour is a little ugly. It would better if startAsync() and startAsync(req,res) had different return types to represent the different styles, but I have used ISE to keep this as a minimal set of changes to the PR proposal to achieve the desired semantics.