Motivation
Currently GWT contains the very useful RPC mechanism, as a means to communicate with services hosted on a J2EE server. For circumstances where it is more desirable to wait for the server to push rather than poll the comet technique is surprisingly useful. Because of the way comet is typically implemented as a long lived HTTP connection, an application server with a lot of comet clients will quickly have its thread pool exhausted.
Solution
The rocket framework combines the object serialization support provided by GWT and its own client and server glue to provide a comet solution. A number of customisations were required to make all the browsers work and this is taken care of. Each time a new object is pushed down the callback registered on the client is provided with the deserialized incoming object. Behind the scenes the client takes care of other issues such as reestablishing broken connections and so on.
Technical notes
- A comet session is started with the creation of a hidden iframe which connects to the comet server servlet.
- The servlet periodically pushes either serialized objects or heartbeats. The iframe page gradually continues to grow due to the incoming data.
- Every so often the server disconnects, so the client can close the iframe in order to reclaim memory.
- The client will attempt to reconnect automatically when a disconnect occurs.
- Any java servlet container including tomcat may be used.
Getting started
The following steps below list what and why is needed to use this facility in your own development efforts.
Importing the rocket module
Add the rocket module to your own application module.
<inherits name="rocket.remoting.Remoting" />
Authoring the server component
A servlet must be created which extends rocket.remoting.server.CometServerServlet and also implements the poll() method. The poll method is responsible for blocking and also pushing new objects to the client via the push method. The server may also terminate a comet session by calling terminate().
It is recommended that the poll method block for a short but not too long period time in such a manner as too avoid creating a a busy loop.
Web Container Configuration
The web.xml fragment below gives an example of how to configure a Comet servlet.
<servlet>
<servlet-class>rocket.remoting.test.comet.server.TestCometServerServlet</servlet-class>
<servlet-name>TestCometServlet</servlet-name>
<init-param>
<param-name>maximum-bytes-written</param-name>
<param-value>65536</param-value>
</init-param>
<init-param>
<param-name>connection-timeout</param-name>
<param-value>60000</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>TestCometServlet</servlet-name>
<url-pattern>/comet</url-pattern>
</servlet-mapping>
The above xml demonstrates several important aspects being confugured.
- Associate the comet servlet with a logical name.
- The maximum-bytes-written parameter controls the number of bytes that are written before a comet http connection is recycled (closed and reopened).
- The connection-timeout parameter controls how long a http connection is kept open before it is recycled (closed and reopened).
- The comet servlet must be mapped to a known url. This url will be used by the client.
Because a hidden iframe is used to the time and amount of data accumulated by the hidden iframe page must be managed and reclaimed. In the case of the above example any http connection is closed after 60 seconds or 65536 bytes are written, which ever comes first.
Authoring the client component
A number of steps must be performed to complete
- Create a class that extends rocket.remoting.client.CometClient.
- Create an abstract method called createProxy with annotation that communicates the payload type.
- Register a rocket.remoting.client.CometCallback with the client to handle any incoming payload, server initiated termination or general failures (exceptions)exceptions.
Sample
The sample below shows a comet client and server.
The model
package example.client;
public CometPayload{
CometPayload( int counter ){
this.counter = counter;
}
public int getCounter(){
return counter;
}
}The Server
The sample shows a server class that continually increments a counter pausing for a second between each query.
package example.server;
import rocket.remoting.server.comet.CometServerServlet;
public SampleCometServletServlet extends CometServerServlet{
protected void poll( CometConnection cometConnection ){
try{
Thread.sleep( 1000 );
} catch ( InterruptedException ignore ){
}
cometConnection.push( new CometPayload( this.counter++ ));
}
int counter;
}The client
A number of types must be created as part of the process of creating a comet client.
Sample
The sample class below includes an example of the implemented abstract CometClient method. The client may be used to control a single comet session via the start() and stop() methods.
package example.client;
import rocket.remoting.client.CometClient;
import rocket.remoting.client.CometServerConnectionFailureException;
/**
* @comet-payloadType example.client.CometPayload
* This has been promoted to a class annotation.
*/
public ExampleCometClient extends CometClient{
};Starting a comet session
The code sample below shows how to create and start a comet session with the server.
ExampleCometClient client = (ExampleCometClient)GWT.create( ExampleCometClient.class );
client.setServiceEntryPoint( "servlet-url" );
client.setCallback( new CometCallback(){
public void onPayload( final Object incoming ){
CometPayload payload = (CometPayload) incoming;
// do something with payload...
}
public void onFailure( final Throwable cause ){
// handle problem
}
public void onTerminate(){
// server has terminated session...
}
});
client.start();Further samples
For further examples refer to the unit test.
- client - rocket.remoting.test.comet.client.CometTest
- server - rocket.remoting.test.comet.server.TestCometServerServlet
To run the comet demo the embedded tomcat must be running.
Since the new version 0.36, you don't have to create two service interfaces to make the RPC thing work.
Instead, i see that you can now use this:
public ExampleCometClient? extends CometClient?{
};But how can this work? The Client class itself should now be made abstract. And if that's the case, how do I instantiate it?
@Cheeysoft
I have updated the wiki so that the line to instanciate a ExampleCometClient? now uses deferred binding (GWT.create(ExampleCometClient?.class );
Thank you! One more correction: The class ExampleCometClient? should be declared abstract in order to make it compile
Have you guys ever tested this framework in GWT web mode? It seems to work in hosted mode only :(
про
Hi all! First of all, many many many congratulations for this fantastic piece of code!, it rocks! I'm doing some tests with Comet. I was able to send a mockup object that has an ArrayList? attribute and a HashMap? attribute and everything worked fine. The thing is that I have a DTO from a project I'm working on and apparently it doesn't reach the onSuccess method. My question is: what are the requirements for an object to be transmitted to the browser successfully (e.g. implement IsSerializable?). Maybe you could add it to the Wiki. Tx in advance. Cheers!
Can a CometClient? only accept a single payload type ?
If I understood well, the code is doing something like:
` while(true) {
} `Isn't this an active wait? It looks like the server is constantly pooling an object for new data. Or have I missed some detail of the implementation?
Yes the servlet is polling that particular method. Your queryObjectSource() method is free to block and do whatever before passing back a response. However the blocking shouldnt be for a long period of time ( say minutes) but rather should use a short value like a second or so.
Until java has continuations theres no way around it for now.
@cbriad, Well partially you can make the payload an interface, and have many impls.
@ arikogan ouch didnt notice your post, sorry for some reason comments posted here dont seem to be emailed to me.
Only serializable types may be transmitted. I will update wiki to note this.
I have integrated the test application in eclipse, in hosted mode it works fine. But if i try to compile and open in IE6 I get an error. Has somebody tested this framework in GWT web mode? It seems to work in hosted mode only.
I tried the example and it works just fine under I.E. 6 (Windows) for me, but under Firefox 2.0.0.12 and Firefox 3 Alpha 4 (Linux) the code works until the server decides to reset and then a failure is thrown on the client. The throwable message states that the client is "Unable to connect to <comet servlet url>". Has anyone else experienced this behavior or does anyone have any ideas about how to debug the problem. Thanks.
How can this method return CometPayload? if the return type is void ? <pre> protected void poll(){ try{ Thread.sleep( 1000 ); } catch ( InterruptedException ignore ){ } return new CometPayload( this.counter++ ); } </pre>
instead of returning the payload, like in the old implementation you use simple
this.push(payload); when you want push something
I created an abstract class that extends CometClient? and using deferred binding (GWT.create(MyTestCometClient?.class )) to instantiate it. But, it will create a runtime error saying "Deferred binding result type 'xyz.MyTestCometClient?' should not be abstract"
Should the class not be abstract (it is abstract in the remoting test project)? Or, am I doing something totally wrong?
Thanks!
Can you explain how your method resolves the one thread per connection issue?
I am trying to get my mind around exactly what problem rocket-gwt solves. I am new to gwt and for that matter web app development. I am more familiar with thick client technologies, i.e. Swing.
What I am attempting to do is (optionally) replace an existing Swing gui with a GWT implementation. What I am looking for is a way to, from the server's perspective, get data from the GUI, set data in the GUI and receive events from the GUI using GWT.
So I need a server-push solution because both get/set data from/to the GUI require server-push where receive GUI user events does not.
As I understand rocket-gwt it keeps one channel open where the server can 'block' until it has something to send (but maybe not because the docs seem to say to not block too long??). So this would let me send data to the client when the server has something to send but how can I get data from the client? For instance I may need to know some value they have entered (optionally I suppose the server could keep track of all that is in the GUI but that is not how a Swing app would work...trying to keep the design the same.)
As for the comet channel, do I have to do the multiplexing so the payload gets to the right place on the client? Or is there a way where comet-gwt can deal with multiplexing?
Thanks, Dave
After implementing a test of comet based on the unit test code it all compiles but I get a failure just after starting the comet service. Here is the log:
C:\cvs\CVSTAS\core>CollaborationContactsView?-shell.cmd Set comet entry point to: http://localhost:8888/ipt.tas.collab.ui.CollaborationContactsView/comet Starting comet service. Client is starting new session... Started comet service. Set collaboration entry point to: http://localhost:8888/ipt.tas.collab.ui.CollaborationContactsView/action onFailure() rocket.remoting.client.CometException?: Unable to connect to "http://localhost:8888/ipt.tas.collab.ui.CollaborationContactsView/comet".
I have the comet service (and my non-comet service) defined in my gwt.xml file as: <servlet path="/comet" class="ipt.tas.collab.ui.server.TestCometServerServlet"/> <servlet path="/action" class="ipt.tas.collab.ui.server.RemoteServiceCollaborationContactsView"/>
What am I doing wrong? Thanks.
Can somone make a simple hello world example and upload? Its really not working for me.
did you mean this when you said to create abstract method ( i am confused and my app giving error)
public abstract class ExampleCometClient? extends CometClient? {
};The cod you have written under the heading "Sample" is not complete or not right. Which is confusing.
I've begun working on an implementation for Grizzly Comet and GWT. Check out <a href="http://www.javascriptr.com/2008/05/28/gwt-grizzly-comet/">here</a>
I've begun working on an implementation for Grizzly Comet and GWT. Check out http://www.javascriptr.com/2008/05/28/gwt-grizzly-comet/here
Do you know that this example does'n work? I try run it on Gwt 1.5 with rocket for 1.5. This sample isn't clear to understand and I think you miss good example, please remake this code to more understanded :) Curently I looking for other solution
Regards, Adrian
hyh, i cant run this example too :/ if you can, please remake it to fully working program
I'm having problems with GWT 1.5 too. Should the class that extends CometClient? be abstract? If not, what should the method "dispatch" do?
import rocket.remoting.client.GwtSerializationCometClient?;
/
- @comet-payloadType example.client.CometPayload?
-
public abstract class CometClientImpl? extends GwtSerializationCometClient? { }I am starting using comets on GWT 1.5.1 Actually I can't see the examples which according to Miroslav should be in the packages:
rocket.remoting.test.comet.client.CometTest? rocket.remoting.test.comet.server.TestCometServerServlet?
Can anyone say where I find them? Thanks
A trivial question maybe, but can I send a request object to the server when establishing the session? I need the client to send a job to the server and then use the long-lived connection to receive updates on that job from the server.
Alternatively, if I need to send my request object out-of-band, how do I match the comet connection with the proper client?
cheers, Erik van Zijst
Can somebody provide a working example? Thanks!
I'm looking for a working example of Rocket with GWT1.5 ! Thanks a lot! Cheers!
@ miroslav.pokorny
Hi, nice work! But I was seriuos dificult to run a simple example of comet (like a push some messages with timestamp). Where am I found some simple example like this? (something simple and thin, like a "hello comet" )
PS.: some codes of this wiki page are wrong! Look some anotations that I did in: http://www.diigo.com/annotated/b4553f25cdf18dd0f8918f9cbb6ddef4
thanks and congrats!
@miroslav.pokorny In the MotivationSection? you describe the problem of the exausted thread pool. But as zaitz3v wrote, your server does an active wait. So I do not see any sense in using the rocket.remoting framework to avoid this. Btw, since the http protocol does not guarantee a flush, the reuse of one connection for more than one "push" is regarded as an unsafe practise. Long polling seems to be the only reliable push concept.
I am getting the following error message while compiling an entry point code with rocket. Any help would be appreciated.
Compiling module com.text.MyEntryPoint Validating newly compiled units [ERROR] Errors in 'jar:file:/C:/.../lib/Rocket.jar!/com/google/gwt/user/client/impl/SafariEventSupport.java' [ERROR] Line 38: The method eventGetKeyCode(Event) is undefined for the type DOMImplSafari [ERROR] Errors in 'jar:file:/C:/.../lib/Rocket.jar!/rocket/remoting/client/support/comet/SafariCometSuport.java' [ERROR] Line 52: missing ) after argument list [ERROR] Unexpected java.lang.NoSuchMethodError: com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.precompile(Lcom/google/gwt/core/ext/TreeLogger;Lcom/google/gwt/dev/cfg/ModuleDef;Lcom/google/gwt/dev/jdt/RebindPermutationOracle;[Ljava/lang/String;[Ljava/lang/String;Lcom/google/gwt/dev/jjs/JJSOptions;Z)Lcom/google/gwt/dev/jjs/UnifiedAst; at com.google.gwt.dev.jjs.JavaScriptCompiler.precompile(JavaScriptCompiler.java:32) at com.google.gwt.dev.Precompile.precompile(Precompile.java:522) at com.google.gwt.dev.Precompile.precompile(Precompile.java:414) at com.google.gwt.dev.Compiler.run(Compiler.java:201) at com.google.gwt.dev.Compiler$1.run(Compiler.java:152) at com.google.gwt.dev.CompileTaskRunner.doRun(CompileTaskRunner.java:87) at com.google.gwt.dev.CompileTaskRunner.runWithAppropriateLogger(CompileTaskRunner.java:81) at com.google.gwt.dev.Compiler.main(Compiler.java:159)It might be easier to just use the Channel API that's coming
http://code.google.com/events/io/2010/sessions/building-real-time-apps-app-engine-feed-api.html
This documentation is terrible, full of typos and far too sparse. I find this generally with GWT. Not only is the whole technology massively overly complex the documentation from official to the opensource world is just plain crap.
Please if anyone wants this technology to actually take off, make a consolidated effort in improving these aspects. Compare any of this to say the Spring documentation and its an absolute disgrace
I agree with the comments about GWT complexity and bad documentation.