RSS

Comet and GWT?

23 Sep

Scala-style Actors in GWT

Another day, another GWT extension.

Recently, on the Chronoscope mailing list, some users have been asking for the ability to stream in updates to the chart, a typical use case being streaming live stock market data. There are issues in Chronoscope blocking this at the moment that require some extensions, but the greater question of how to do live updates of a GWT application got me thinking about how to appropriately model this in GWT.

RPC? Comet? JSON?

So, ordinarily, in the interest of time, I’d take the road most traveled and simply use GWT RPC, some JSNI Comet integration, or some other simple mechanism to receive poll or push data. However, over the last few months, I’ve really fallen in love with Scala, and I like Scala’s abstraction for message passing: Actors.

I like actors for several reasons. First, they are asynchronous by default, hide the underlying implementation (threads, event queues, in-process calls), and well… I just like em because they’re cool.

Actors in Java

Unfortunately, Java doesn’t support the elegant syntax of Scala, but with a Generator to implement multiple dispatch, it’s not too bad. The approach I’ve taken is to use overloaded receive methods to implement pattern-matching case classes:

public class Pinger extends Actor { public void receive(Pong p) { // pong case sendTo(sender, new Ping()); } public void receive(Stop s) { GWT.log("Received a stop message!"); } } public class Ponger extends Actor { int count = 0; public void receive(Ping p) { if(count++ < 100) sendTo(sender, new Pong()); else sendTo(sender, new Stop()); } }

A GWT Generator supplies a compile time dispatch() method which can take dequeued mailbox messages and invoke the appropriate receive() method. Java doesn’t have case classes, so you can use Enums or Type-Safe Enum Pattern as messages (as long as they implement org.timepedia.actors.client.Event)

Big deal, Whoop-de-do

What does all this gain us? Well, for one, it abstracts away the RPC mechanism on the client and server. You don’t need to deal with RPC interfaces, or AsyncCallback, in fact, RPC can be replaced with Comet for the receive channel if you so desire.

However, the big whoop-de-do is the ability to do peer-to-peer messaging between browsers. Chat? Multiplayer gaming anyone?

Remote Actors

In order to extend actors outside the Javascript environment of your browser, we need to use a server side mailbox server to queue and relay messages between actors in different browsers. In order to publish your actor so that remote actors can retrieve it, all you need to write is:

RemoteActors.register(“myId”, actor);

and in another peer, you can write:

RemoteActors.select("myId"); public .... extends Actor { public void receive(ActorEndpoint endpoint) { ... } }

Wait!? There’s no return value? Remote selection is asynchronous (needs to ask mailbox server) so we simply return the result of the select() query as a message using the actor interface!

The returned endpoint represents either a local (in browser), server, or browser peer actor (on someone else’s computer).

Chat using Actors

So let’s say we want to implement a GTalk chat-like program in GWT, with buddy lists, group chat, and private messages. How would we do this with actors?

public class ChatActor extends Actor { public void onStart() { super.onStart(); RemoteActors.select("chatLobby", this); } public void receive(ActorEndpoint rae) { chatLobby = rae.getActor(); sendTo(chatLobby, new JoinLobbyMessage()); } public void receive(ChatMessage p) { html.setHTML(html.getHTML()+" "+p.getMessage()); } public void onChange(Widget sender) { sendTo(chatLobby, new ChatMessage(((TextBox)sender).getText())); } }

The above code assumes we are using an HTML Widget to store the chat transcript, and a TextBox to handle user input. We start out by selecting the “chatLobby” actor from the server. We don’t know where this Actor really resides, it could be someone’s PC, but for my test implementation, it resides in a Servlet.

When someone enters text, we send a “ChatMessage” object to the “chatLobby” actor containing the text. What is chatLobby?

public FooServlet extends HttpServlet ... { public void init() { ChatLobbyActor lobbyActor = new ChatLobbyActor(); RemoteActors.register("chatLobby", lobbyActor); } } public class ChatLobbyActor extends ReflectionActor { HashSet<Actor> actors=new HashSet<Actor>; public void receive(JoinLobbyMessage msg) { actors.add(sender); sendTo(sender, new ChatMessage("Welcome to Chat Lobby")); } public void receive(ChatMessage msg) { for(Actor act : actors) sendTo(act, msg); } }

Implementation details

The library isn’t ready for release right now, but the mailbox routing servlet uses transient memory arrays to hold per-actor mailboxes (with UUIDs to identify them uniquely). This could be released with transient or persistent JMS queues in some implementations. For communication channels, it uses GWT RPC at the moment. It polls the server periodically for messages, and during the send() operation, it receives all pending messages and sends all pending messages to minimize the number of RPC requests. However, I would like to have a Comet implementation before release that gives you the option of receiving messages pushed on a comet channel.

Holy Grail

I have an itch, a real bad, unproductive itch, to implement a multiplayer graphics game in GWT using Chronoscope’s Canvas, Fred Sauer’s GWT-Sound library, and this GWT Actors library. And I’m not talking turn based action, but real, predictive physics simulation, such as a 2D space spacewar/omega-race style shooter.

But if I start messing around with this, I’ll never finish this blog series, nor fix the Chronoscope bugs, nor launch Timepedia. Argh!

In an upcoming article, I’ll talk more about the implementation under the hood, and hopefully release the code.

-Ray

Advertisements
 
Leave a comment

Posted by on September 23, 2008 in COMET

 

Tags: ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: