New Code in Gwittir (svn)
Submitted by kebernet on Tue, 03/03/2009 - 20:51
Tagged:
So this is the first big new thing in a long time. It stems from some work I was just recently doing.
So, basically there is a problem with GWT's basic RPC: If you are trying to send back large blocks of data or you are trying to deal with medium sized chunks of data on a machine with limited specs the cycle of "Read all data in on the server -> Serialize -> Send To Client -> Deserialize -> Render" can take large amounts of RAM on the server to deal with the request, and large amounts of time on the client to deserialize and set up the UI. Enter the StreamingService.
The StreamingService works almost exactly like the standard RPC system, except results are sent back one at a time.
To build a new system, you start much like you would with RPC, except the return type of all your methods should be a typed "StreamServiceIterator". This is a standard iterator with a close() method on it that will get called so you can clean up after you are done (close database connections, release entity managers, etc).
In the example code, first the service def:
package com.totsp.gwittir.example.client;
import com.totsp.gwittir.client.stream.StreamingService;
import com.totsp.gwittir.client.stream.StreamServiceIterator;
public interface ExampleStreamService extends StreamingService {
public StreamServiceIterator<MyClass> getResults(int count,
String name);
}
Then an Async version. The Async versions will return a StreamControl object. This object exposes only a .terminate() method right now. This allows you to cut off the stream early. It also, like the standard Async, takes a callback as the last argument.
package com.totsp.gwittir.example.client;
import com.totsp.gwittir.client.stream.StreamControl;
import com.totsp.gwittir.client.stream.StreamServiceCallback;
public interface ExampleStreamServiceAsync {
public StreamControl getResults(int count, String name,
StreamServiceCallback callback);
}
Next, to call the stream service, it works much like the onSuccess()/onFailure() in RPC, except each individual object will give you a call into the code, and an onComplete() will be called when all the data has been sent.:
ExampleStreamServiceAsync ser = (ExampleStreamServiceAsync) GWT.create(ExampleStreamService.class);
StreamingServiceStub stub = (StreamingServiceStub) ser;
stub.setServicePath(GWT.getModuleBaseURL()+
"ExampleStreamService");
ser.getStrings(5, "foo",
new StreamServiceCallback<MyClass>(){
public void onReceive(MyClass object) {
Window.alert("Got: "+object.getName());
}
public void onError(Throwable thrown) {
Window.alert("Thrown: "+thrown.toString());
GWT.log("", thrown);
}
public void onComplete() {
Window.alert("complete.");
}
});
Finally, to build your server code, you extend the StreamServiceServlet, rather than RemoteServiceServlet, and implement your methods:
public class ExampleStreamServiceServlet extends StreamServiceServlet implements ExampleStreamService {
public StreamServiceIterator<MyClass> getResults(
final int count, final String name) {
System.out.println( count + name );
return new StreamServiceIterator(){
int i=0;
public boolean hasNext() {
return i < count;
}
public MyClass next() {
i++;
MyClass mc = new MyClass();
mc.setName(name + i);
return mc;
}
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void close(){
System.out.println("closed.");
}
};
}
}
How does this whole thing work? Well, rather than use XHR and send all the data and get a single event back, it creates an invisible form and iframe on the page, and POSTs the request to the server. The server then uses a Comet-style callback from the iframe to send each serialized object into the application. Since the script thread releases after each one, and hopefully an individual one will go fairly fast, this lets you maintain UI usability while the data is still coming in from the server -- which is great on slow machines. It also doesn't require a "big create" to RAM, usually looping a Collection to create UI elements, meaning that unused objects that only go to display can fall away quickly.
There are a few known bugs in it right now, but it is good for experimental use. 






Comments
Interesting approach, but
Don't you want too much from JS dealing with deserialization?
I think the point of GWT-RPC
Clever
It is an interesting post,