Tuesday, December 11, 2007

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

6 comments:

Robert Hanson said...

> 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.

Maybe you should wait for Fred to release his game engine. I haven't seen it yet, but if it is like his other works it won't be shabby. He used it to build the Hornet Blast game.

zproxy said...

Looking forward for the game :)

alsdkfj said...

Re actors architecture for Java (and Squeak and Lisp), you might want to take a look at E. Lots of good ideas - if you can't use the code you may still be able to learn from what they've done. It has both a scripting language (which you could choose to ignore) and an API (which could be used from Java). Unfortunately I don't believe there's a Javascript binding.
http://erights.org/

Raoul Duke said...
This comment has been removed by the author.
Raoul Duke said...

[argh, formatting woes]

(a) pretty please release your gwt-actors stuff; it would be pretty cool to see that snowball start rolling!

(b) re: E-lang, there is Caja which is E-in-Javascript.

Anonymous said...

(法新社倫敦四日電) 英國情色大亨芮孟a片的公司昨天說,芮孟av日前成人影片av女優世,享壽八十二歲;這位身價上億的房地產日本av開發商,部落格a片經在倫成人av推出第一場脫衣舞表演。

成人網站
芮孟的財產估計av女優達六億五千萬英鎊成人影片(台a片av女優情色近四成人百億),由於他名下事業大多分布在倫敦夜生色情a片色情區蘇活區sex,因此擁有「蘇成人網站情色之王」的稱號。
部落格

他的公司「保羅芮成人影片孟集團」旗下發a片行多種情色雜誌,包括「Raavdvdzzle」、情色電影「男性世界」以及「Mayfair」。色情影片


芮孟本名傑福瑞.安東尼.奎恩,父親色情為搬運承包商。芮孟av成人光碟五歲離開學校,矢言要在表演事業留名,起先表演讀av心術,後來成為巡迴歌舞雜耍表演的製作人。


許多評a片下載論家認為,他把情色情色電影表演帶進主流社會成人電影,一九五九年主成人網站持破情色視訊天荒的脫衣舞表演,後來更靠著在蘇活部落格區與倫敦色情西區開發房地產賺得大筆財富。

a片下載
有人形成人電影容芮孟是英國的海夫納,地位AV片等同美國的「花花公子」創辦人海夫納。