I have reconsidered the API for asynchronous servlets that I proposed in my recent blog – it is not good!. It makes some of the same mistakes as weblogic and tomcat have made with their asynchronous extensions.
So let’s have a brief review of all the available and
proposed solutions.
BEA WebLogic
BEA added AbstractAsyncServlet in WebLogic 9.2 to support threadless waiting for events (be they Ajax Comet events or otherwise eg. an available JDBC connection from a pool). Their API separates the handling of a request and the production of a response into doRequest
and doResponse
methods. A call to the a notify
method or a timeout triggers the invocation of the doResponse
call.
This API can certainly be used to handle most of the important use-cases that I have previously discussed, but it suffers from several major flaws:
It’s not really a servlet – The user cannot implement doGet
or service
methods to generate content so there is limited benefit from tools or programmer familiarity. Furthermore, the javadoc states an AbstractAsyncServlet “cannot be used as the target of request dispatching includes or forwards... Servlet filters which get applied before AbstractAsyncServlets, will not be able to take advantage of post processing of the response.“.es
So an AbstractAsyncServlet cannot be used like a servlet and the URLs that it served will not be able to be re-used (eg. in portlets) and it cannot live behind common filters that may apply aspects (eg security, authentication, compression) . It is a cuckoo in a nest of servlets, only pretending to be a servlet and breaking all the other eggs in the process.
There can only be one. There are many reasons that a wait may be required while handling a request and all are candidates for asynchronous waiting. But as this solution is tied to a single Servlet, either it will have to implement all the waiting concerns or there can only be one efficient wait. For example, it would be unreasonable for a single servlet to implement async waits for a remote authentication server and the arrival of a JMS message and for an available JDBC connection from a limited pool.
It is not portable and servlets that implement this API will fail on containers that do not support it.
I believe AbstractAsyncServlet is a good solution for a particular async use-case, but it is not a candidate as a general approach.
Tomcat 6.x
After my initial blogging on this issue, the tomcat developers added CometProcessor and CometServlet (unfortunately without engaging in an open discussion as I had encouraged). It is essentially the same solution as BEAs, but with a few extras, a few gotchas and the same major issues.
It is still a special type of servlet and the begin
and end
methods take the
place of BEAs doRequest
and notify
calls. Asynchronous code directly calls the response object until it a call to end
indicates the end of the handling of the request.
The badly named CometProcessor
(Comet is only one use-case for async servlets) does support asynchronous IO, but I’ve argued there is little need for this and it would be better to get the container to do IO if there was.
The start
method does execute in the context of the Filters mapped to the servlet/URL. However, there is no support for the asynchronous code itself to operate within the context of the filter chain. Thus any non-trivial filters that are unaware of the tomcat mechanism are unlikely to work. Any authentication information or other actions taken by filters will not apply to the code that generates the response. So like the BEA solution it is not a servlet except by name and would not be able to be the used with arbitrary dispatches or generic filters.
There still can only be one and multiple asynchronous aspects may not be combined with this API.
The implementation makes a naive attempt at portability
and will call the begin
and end
methods in a container that does not support the mechanism. But if the implementation of begin
schedules asynchornous writers to the response object (as it should), then this breaks the servlet contract and simply will not work as the response will be committed long before any asynchronous handling.
ServletCoordinator
My proposed ServletCoordinator suffers from many of these same issues. It does meet one of my main concerns in that responses are generated by normal servlets code using normal techniques and within the scope of the applicable filter chain. But there still can only be one and there is no support for multiple asynchronous aspects. It avoids being an ugly ducking servlet, but only by not being called a servlet. It is still a new non portable mechanism that is unlikely to work with arbitrary dispatchers
It’s not a cuckoo, it’s a dodo!
Jetty 6 Continuations
The Jetty 6 Continuation mechanism is not an extension to the Servlet API. Instead it as a suspend/resume mechanism that operates within the context of a Servlet container to allow threadless waiting and reaction to asynchronous events by retrying requests.
Continuations well address the concerns I have raised above:
- Request handling and response generation is done within normal servlets and always within the scope of the applicable filter chain. Common tools and frameworks can be used without modification.
- If RuntimeExceptions are propogated and the stateless nature of HTTP respected, then there is a reasonable expectation that arbitrary filters and dispatchers may be applied.
- There can be multiple asynchronous concerns applied as each may independently use a continuation. For example, it is possible to apply the ThrottlingFilter in front of the activemqAjaxServlet and while both use Continuations, neither will interfer with the other.
- It is truely portable and if run within a container that does not support Continuations, will fall back to threadful waiting.
While some (including myself) are a little perturbed by the way RuntimeExceptions are used by Continuations, I argue that should be seen as an implementation detail and that the semantics of the API are correct for the purpose. There are already byte-code manipulating continuation solutions available and rumours of future JVM support, so the implementation can be improved.
Thus I have not yet seen a better API than continuations for the majority of the asynchronous use-cases within the
servlet model. More over, I believe that the async APIs of BEA and tomcat can be trivially implemented in a container supporting continuations, but that the inverse is not true.