Google Web Toolkit
The Google Web Toolkit allows Ajax applications to be developed in java code using the traditional UI widget paradigm. The toolkit includes support for RPC, but not for comet style Ajax push.
Unfortunately GWT has not made it easy to use continuations within their RPC mechanism. Firstly they catch Throwable, so the Jetty RetryException is caught. Secondly they have made most of the methods on the GWT servlet final, so you cannot fix this by extension.
Luckily GWT is open sourced under the apache 2.0 license, so it was possible to do a cut/paste/edit job to fix this. The OpenRemoteServiceServlet recently added to Jetty is a version of GWTs RemoteServiceServlet without the final methods and a protected method for extending exception handling.
Once the GWT remote service servlet has been opened up, it is trivial to extend it to support Continuations, which has been done in AsyncRemoteServiceServlet.
Because GWT RPC uses POSTS, the body of the request is consumed when the request is first handled and is not available when the request is retried. To handle this, the parsed contents of the POST are stored as a request attribute so they are available to retried requests without reparsing:
protected String readPayloadAsUtf8(HttpServletRequest request)
throws IOException, ServletException
{
String payload=(String)request.getAttribute( PAYLOAD );
if ( payload==null )
{
payload=super.readPayloadAsUtf8( request );
request.setAttribute( PAYLOAD,payload );
}
return payload;
}
The exception handling is also extended to allow the continuation RetryException to propagate to the container. This has been done without any hard dependencies on Jetty code. With these extensions, the AsyncRemoteServiceServlet allows any GWT RCP method to use continuations to suspend/resume processing. All you need to do is to have your servlet implementation extend the org.mortbay.gwt.AsyncRemoteServiceServlet:
package com.acme.example;
import org.mortbay.gwt.AsyncRemoteServiceServlet;
import org.mortbay.util.ajax.Continuation;
import org.mortbay.util.ajax.ContinuationSupport;
public class MyServiceImpl extends AsyncRemoteServiceServlet implements MyService
{
}
For example below is a Table class from http://www.gpokr.com where a continuation is used by the service implementation to wait for an event to be available for a player:
class Table
{
Set waiters = new HashSet();
public Events getEvents( Context c )
{
Player p = getPlayer( c );
if( p.events.size() == 0 )
{
synchronized( this )
{
Continuation continuation =
ContinuationSupport.getContinuation
( c.getRequest(),this );
waiters.add( continuation );
continuation.suspend( 30000 );
}
}
return p.events;
}
protected void addEvent( Event e )
{
Iterator it = players.values().iterator();
while( it.hasNext() )
{
Player p = (Player)it.next();
player.events.add( event );
}
synchronized( this )
{
Iterator iter = waiters.interator();
while ( iter.hasNext() )
( (Continuation)iter.next() ).resume();
waiters.clear();
}
}
}
You will need to build and then include the jetty-gwt-<version>.jar in your webapp. The source is included with every download of jetty. To build:
> cd $jetty.home/extras/gwt
> mvn install