Salut,
The Servlet 3.0 is currently working on adding some APIs for supporting
what our Comet framework allow for awhile: suspend and resume the
response. The E.G is still discussing some details, but since GlassFish
will eventually supports those API, I've added new API on the Response
object so we can support GlassFish (Servlet Container), but also all
projects that use Grizzly and want to be able to suspend/resume
request/response (Jersey, GlassFish Rails and Grails extension, etc.)
I didn't implemented the Servlet 3.0 spec directly (will eventually
implement it under the http-servlet module) but instead created a new
set of API (which I consider much more easier to use that Servlet 3.0
proposal ;-)).
Neither I did implement those API on top of Grizzly Comet as it would
have means hiding a lot the framework features just to just support
suspend/resume. Of course the Grizzly Comet Framework could used the new
set of API (just recompile using the new API, but I do think the
framework is more powerful by running on top of the Grizzly Asynchronous
Request Processing API instead of this new one.
Anyway long story to introduce the new API, which is on
com.sun.grizzly.tcp.Response [1]
com.sun.grizzly.tcp.http11.GrizzlyResponse [2]
com.sun.grizzly.tcp.CompletionHander [3]
I've added multiples examples in:
com.sun.grizzly.http.SuspendTest [4]
Using the Response object as an example:
> /**
> * Resume the {_at_link Response} and finish/commit it. If a
> * {_at_link CompletionHandler} has been defined, its {_at_link CompletionHandler#resumed(A)}
> * will first be invoked, then the {_at_link Response#setCommitted(Boolean)} followed
> * by {_at_link Response#finish()}. Those operations commit the response.
> */
> public void resume(){
>
> /**
> * Cancel the {_at_link Response} and finish/commit it. If a
> * {_at_link CompletionHandler} has been defined, its {_at_link CompletionHandler#cancelled(A)}
> * will first be invoked, then the {_at_link Response#setCommitted(Boolean)} followed
> * by {_at_link Response#finish()}. Those operations commit the response.
> */
> public void cancel(){
>
> /**
> * Return <tt>true<//tt> if that {_at_link Response#suspend()} has been
> * invoked and set to <tt>true</tt>
> * @return <tt>true<//tt> if that {_at_link Response#suspend()} has been
> * invoked and set to <tt>true</tt>
> */
> public boolean isSuspended(){
>
> /**
> * Suspend the {_at_link Response}. Suspending a {_at_link Response} will
> * tell the underlying container to avoid recycling objects associated with
> * the current instance, and also to avoid commiting response.
> */
> public void suspend(){
>
> /**
> * Suspend the {_at_link Response}. Suspending a {_at_link Response} will
> * tell the underlying container to avoid recycling objects associated with
> * the current instance, and also to avoid commiting response.
> *
> * @param timeout The maximum amount of time, in milliseconds,
> * a {_at_link Response} can be suspended. When the timeout expires (because
> * nothing has been written or because the {_at_link Response#resume()}
> * or {_at_link Response#cancel()}), the {_at_link Response} will be automatically
> * resumed and commited. Usage of any methods of a {_at_link Response} that
> * times out will throw an {_at_link IllegalStateException}.
> *
> */
> public void suspend(long timeout){
>
> /**
> * Suspend the {_at_link Response}. Suspending a {_at_link Response} will
> * tell the underlying container to avoid recycling objects associated with
> * the current instance, and also to avoid commiting response. When the
> * {_at_link Response#resume()} is invoked, the container will make sure {_at_link CompletionHandler#resumed}
> * is invoked with the original <tt>attachement</tt>. When the
> * {_at_link Response#cancel()} is invoked, the container will make sure {_at_link CompletionHandler#cancelled}
> * is invoked with the original <tt>attachement</tt>. If the timeout expires, the
> * {_at_link CompletionHandler#cancelled} is invoked with the original <tt>attachement</tt> and
> * the {_at_link Response} commited.
> *
> * @param timeout The maximum amount of time, in milliseconds,
> * a {_at_link Response} can be suspended. When the timeout expires (because
> * nothing has been written or because the {_at_link Response#resume()}
> * or {_at_link Response#cancel()}), the {_at_link Respons} will be automatically
> * resumed and commited. Usage of any methods of a {_at_link Response} that
> * times out will throw an {_at_link IllegalStateException}.
> * @param attachment Any Object that will be passed back to the {_at_link CompletionHandler}
> * @param competionHandler a {_at_link CompletionHandler}
> */
> public void suspend(long timeout,A attachment, CompletionHandler<? super A> competionHandler){
From an Adapter, you can now suspend and later manipulate the response
object from another thread. As an example, let's assume you extend the
StaticResourceAdapter.service() with the code snippet below:
> if (res.isSuspended()){
> super.service(req, res);
> return;
> }
> res.suspend(60 * 1000,this, new CompletionHandler<StaticResourceAdapter>(){
>
> public void resumed(StaticResourceAdapter attachment) {
> try{
> attachment.service(req,res);
> attachment.afterService(req,res);
> } catch (Exception ex){
> ex.printStackTrace();
> }
> }
>
> public void cancelled(SuspendedAdapter attachment) {
> attachment.service(req,res);
> attachment.afterService(req,res);
> }
> });
>
>
> ScheduledThreadPoolExecutor.schedule(new Runnable(){
>
> public void run(){
> try {
> if (res.isSuspended()){
> res.resume();
> }
> } catch (Throwable ex) {
> ex.printStackTrace();
> }
> }
>
> },5, TimeUnit.SECONDS);
When a request comes in, the Adapter.service will be invoked and the
first operations that will happens to make sure the Response is not
suspended. Next, the response is suspended for a maximum of 60 seconds
(idle). If there is no I/O operations on the Response, the
CompletionHandler.cancelled will be called and the Response resumed and
commited. If the Response gets resumed, then the
CompletionHandler.resumed() will be eventually invoked.
In the example above, I spawn a ScheduledThreadPoolExecutor's Thread
that will asynchronously execute 5 seconds after the Adapter.service()
method has returned. In that example, I just invoke the
Response.resume(), which will make CompletionHandler.resumed() invoked.
In that method, I just re-invoked the Adapter.service() and this time,
since the res.isSuspended() return true (as we are still in the process
of resuming), the request will be serviced. In the demo above, it just
means that every requests will be throttled for 5 seconds before being
serviced. Note that there is no thread that blocks waiting for an event
to happens. All happen asynchronously.
Feedback welcomed! The commit details can be found [5]
-- Jeanfrancois
[1]
https://grizzly.dev.java.net/nonav/apidocs/com/sun/grizzly/tcp/Response.html#suspend()
[2]
https://grizzly.dev.java.net/nonav/apidocs/com/sun/grizzly/tcp/http11/GrizzlyResponse.html#suspend()
[3]
https://grizzly.dev.java.net/nonav/apidocs/com/sun/grizzly/tcp/CompletionHandler.html
[4]
https://grizzly.dev.java.net/nonav/xref-test/com/sun/grizzly/http/SuspendTest.html
[5]
https://grizzly.dev.java.net/issues/show_bug.cgi?id=315