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.

This code is checked into a branch of Jetty and branch of the servlet-api. It includes implementations of following asynchronous filters/servlets:

  • 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.
 
 


3 Comments

Rajiv Mordani · 22/12/2008 at 07:28

Greg these implementations do NOT reflect the changes that have been approved in the EG. If you would like to provide them as Jetty specific APIs you are welcome to do so. Please do NOT represent these as an update the Servlet 3.0.

As for the throwing IllegalStateException when you call forward after a startAsync(req, res) the main problem was that if anything was written to the response it COULD be lost. Similarly the same applies to the startAsync() (original request and response). The forward clears any uncommitted content. So the same should be applied IF you think IllegalStateException should be thrown in the case where you throw ISE in the case of startAsync(req, res) followed by a forward, the same should be applied to startAsync() that passes in the original request response.

Greg Wilkins · 24/12/2008 at 14:37

Rajiv,
I clearly state that these are my suggestions and I don’t say anything to imply that these changes came from the EG.

forward always resets the buffer, so better not to hold data for the duration of the async phase only to discard it. Much better to ISE at the very start and make sure that developers don’t try to go async after they have started a response.

However, if startAync(req,res) was prevented from using forward(), then it would not be a problem, content could be kept and an ISE would not be needed of the response was started when startAsync(req,res) was called.

Ravi Luthra · 11/05/2009 at 16:36

The PFD is out and I feel in the dark about the security concerns. Have they been addressed since your posts on the issue. I am excited about S 3.0 but at the same time I can’t deal with the crisis I would have if I got a commons-logging-1.x from Java.net repository that was deployed by someone that creates a new java.net project in disguise has a /hackmeupbaby/ servlet designed to call home and take commands.

Comments are closed.