Google has quietly put all (most?) of the GWT Conference session videos online now.
Here is my talk on GWT Deferred Binding:
Saturday, December 29, 2007
GWT Conference videos now online
Posted by Timepedia at 11:26 AM 0 comments
Thursday, December 13, 2007
Scala-style Actors in GWT: Architectural details
In the last article, I talked about the targeted API for actors, I'll talk a little about the architecture I am prototyping with.
Store-and-forward messaging
The essential idea for implementing communication between actors is to give them a globally unique address (UUID) first, which is called the ActorEndpoint class. Think of a UUID as the "email address" of an Actor, with Actors themselves being the email client.
Because of limitations in the browser, we can't really use any information about the machine itself in creating these UUIDs, so instead, we create pseudo-version-4 random UUIDs
The Actors.register() registers Actor instances in an internal HashMap so that Actors can be looked up by UUID/Endpoint later.
The only essential difference between a local in-process Actor, and a remote out-of-process Actor, is that the endpoint is a RemoteEndpoint which returns an associated (do nothing) RemoteActor.
So the best way to think of the delivery/receive process is by analog to plain-ole SMTP email queues, except that the "SMTP" server in this case, can only wait for a client to connect, it can't initiate any connections.
Delivery: Local vs Remote
When you invoke Actor.sendTo(anotherActor, msg), it delegates to Actors.addToMailbox(sender, receiver, msg). This code essentially performs a check: if(receiver.getEndpoint() instanceof RemoteEndpoint) it enqueues the message in an envelope addressed to the remote mailbox associated with the UUID of the remote actor.
If the receiver is not a remote actor, and is in the 'started' state, it invokes a method Actor.localReceive() which can process the event immediately. localReceive() delegates to a Generator method (client) or Reflection method (Server) to perform the multiple dispatch.
Transporting the Messages
For outgoing messages, a periodic timer task checks for non-empty outgoing queue, and invokes a GWT RPC call which transmits all enqueued Envelopes, and receives as a result, all messages that had been sitting on the server destined for a local actor in this browser. It then opens the Envelopes received, and uses the UUID of the recipient to lookup the appropriate Actor for local delivery.
For incoming messages, there are two things that can be done: 1) we can poll with RPC for messages. 2) we can use one of the numerous Comet techniques to receive push messages. I'll probably support both in the final library.
The mailbox servlet
All of the communication is orchestrated by a GWT RemoteService that maintains for each UUID, a queue of Envelopes waiting to be delivered. It's currently very simple, transient, and not fail-safe. There is no security for the registrar for globally named actors (e.g. "chatLobby") and no Envelope sequence number to prevent out of order or duplicates, which is sure to happen with Comet + RPC combined channels.
Other cool stuff
Someone has made a JXTA/Scala Actor gateway, which allows messaging across the P2P JXTA network. This could certainly be adapted to GWT Actors. One could also imagine interacting with a JMS Message Queue/EJB MessageBeans, XMPP/Jabber, or Android clients as well. I am trying to take care in making this library 'cloud computing safe' like Chronoscope, so that one day, mobile phone users will be able to play interactive p2p games against Web desktop users. :)
Posted by Timepedia at 11:42 AM 2 comments
Tuesday, December 11, 2007
Playing with feedburner options
I hope this doesn't annoy too many people, or break some subscriber's feeds, but I've been toying around with Feedburner (first time) tonight. Let me know if your feed broke.
-Ray
Posted by Timepedia at 11:57 PM 0 comments
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
Posted by Timepedia at 4:08 PM 6 comments
Monday, December 10, 2007
Deferred Binding slides online
My GWT Conference presentation on Deferred Binding was a little rushed due to time, so here are the slides for those who wish to review the material:Deferred Binding@Google Docs.
I'll post up a PDF version later.
-Ray
Posted by Timepedia at 2:37 PM 2 comments
Sunday, December 9, 2007
Is Volta's Javascript Interop better than GWT?
In this ZDNet Interview with Volta architect Eric Meijer, Eric says:
"The GWT uses Java native methods to interface to JavaScript where the JavaScript implementation of that native method is defined in a special pragma comment.
In many situations, the compiler can automatically infer the JavaScript implementation from the metadata for the corresponding function declaration in C# (or VB). The Volta toolkit therefore implements a sophisticated convention over configuration heuristic to simplify writing foreign function interfaces. As a result this typically enables programmers to import JavaScript functionality by just writing a single [Import] attribute on an extern method signature."
This sounds like Volta's Javascript interop is better than GWT, but is it?
Deferred Binding again?
I must sound like a broken record by now, but GWT's Deferred Binding allows a GWT developer to accomplish anything Volta's interop does, and more. It's a general purpose mechanism to achieve compile time metaprogramming, where Javascript Interop is just the tip of the iceberg. If you want Volta-style no-JSNI interop, here's how to get it:
Bob Vawter's No JSNI Interop
While it is not a part of GWT's core, Bob Vawter, a member of the GWT Team, released a package for no-JSNI interop 'convention over configuration' several months ago.
How does it work? Just make an interface where the method names correspond to actual Javascript object methods and extend JSWrapper, for example, with the GMap2 API:
interface GLatLng extends JSWrapper {
/**
* The naming of the method is arbitrary, the only thing that's important is
* the presence of the gwt.constructor annotation.
*
* @gwt.constructor $wnd.GLatLng
*/
public GLatLng construct(double lat, double lng);
public double lat();
public double lng();
}
The constructor annotation tells the generator how to map the construct() call to the Javascript constructor. JSIO supports tons of additional interoperability features that I'm not sure Volta is capable of.
My GWT Exporter library
In addition to being able to import Javascript classes/functions/fields, one also wants to export Java functions to Javascript in a way that makes them callable from non-Java code. Volta can do this for static functions only it appears via the [Export] declaration.
What if you need to export methods, fields, and interfaces, and have polymorphic method dispatch on exported class instances still work? GWT Exporter makes this possible. In fact, one of the coolest features of GWT Exporter that I like is automatic Javascript closure conversion.
Imagine you have the following Java code:
/**
* @gwt.export
*/
public static void foo(FooCallback callback) {
callback.doIt();
}
/**
* @gwt.exportClosure
*/
public interface FooCallback {
void doIt();
}
Then you can invoke the foo() method with an ordinary Javascript closure:
foo(function() { alert("Hello World") })
And that closure will automatically be converted to an instance of the FooCallback interface and mapped to the doIt() function. Neat huh? I'm not saying Volta can't do this, I'm just not sure because the docs don't mention it.
The sky's the limit
If there's a feature missing from Bob's library, or mine, you don't need to fret and think about hacking the compiler, because GWT's Deferred Binding Generator mechanism allows anyone to extend the system to support whatever annotation and code-injection techniques one desires.
Future versions of GWT are likely to obsolete Bob's library and mine, by simply unifying the JS and Java ASTs in a way that a few extra annotations will allow the compiler to 'understand' a Javascript library and automagically map Java's types on top of it. You'll get even more optimal code then, as GWT will even be able to optimize third party JS libraries.
The title of this article is somewhat flamebait, but I wouldn't want people reading the ZDNet interview to come away assuming that GWT can't handle the kind of Javascript interop that Volta can.
-Ray
Posted by Timepedia at 11:15 PM 2 comments
Friday, December 7, 2007
Cloud Computing and GWT
(At the GWT Conference conclusion, someone asked about authoring apps that run on both GWT and Android, I promised I'd detail my findings here...)
One of the purported benefits of Microsoft Volta is "cloud computing" which is the ability to move code execution to different tiers in your application. Now, cloud computing is not really new per se, since mobile code platforms have been able to achieve this for some time with careful architecture, but Microsoft is presenting a vision of doing it painlessly and (relatively) automatically.
I'm of the opinion that it's probably achievable to do it in an automated fashion, but that you are going to hurt user experience. That's because the raw execution performance and I/O bandwidth of the client and server differ dramatically, and simply moving some business logic and data handling code from server to client without thinking about it is going to lead to plenty of pathologically bad cases. It's a case of trying too hard to plug holes in a leaky abstraction.
Choose an abstraction that leaks least
That said, if you are careful, you can do Cloud Computing with GWT and achieve code reuse without suffering too many performance gotchas or pathologically bad cases, but the design work has to be done up front, as it becomes alot harder later to unshackle dependencies and abstraction leaks from you code.
I don't profess to be an expert on this, but here is my experience from porting Chronoscope to several "clouds" (Mobile, JS, Flash, Java2D).
Know your clouds
First, do some up front legwork before you start coding. Hand code a prototype for each cloud to get an intuitive sense of each platform's performance and API support. Then choose a reasonable lowest-common-denominator in terms of API support and performance.
In the case of Chronoscope, I had written JS, Flash, and Java2d prototypes before I wrote the first line of GWT code.
Scaling APIs
Now that you know what your target minimum requirements are, you have two tasks. First, pick an abstraction that represents what's achievable on each of the platforms you're targeting to make sure enough API support is there to either support a feature directly, or emulate it with acceptable performance.
You may have to do several iterations of this to get it right. For example, with Chronoscope, I started out with the Safari/WHATWG Javascript canvas as my abstraction, but I needed text rendering (rotated as well), hit detection, Flash support, and the ability to not have to redraw unchanged portions of the screen.
I started by adding horizontal text rendering by placing DIVs over the Canvas, which of course has several problems (that I addressed later). I then noticed that to make Flash performant I need to ship a whole frame's worth of drawing commands to Flash in a batch. Moreover, Opera's Canvas incrementally updates the display while you're drawing, but they fixed this by adding a non-standard lockCanvasUpdates() function. This led to the addition of OpenGL-style DisplayList capability and a beginFrame()/endFrame() pair of methods.
I initially tried to emulate damage region painting everywhere, but it was too slow. It turns out creating lots of CANVAS elements or MovieClip objects in Flash is not that bad, so I introduced the concept of Layers (ala Photoshop), with a canvas.createLayer() call. The layer system also facilitates adding hit detection in a way which doesn't require alot of scenegraph-style overhead. I'm now confident that this is the right abstraction not only to support slow cloud platforms, but to leverage natively accelerated features of the various platforms.
Decoupling from GWT
Usage of any com.google.gwt.* classes is going to tie your code to running in the GWT cloud. Now, gwt-user is great and we want to use it, but do so in a way that allows it to be swapped out on other platforms.
For Chronoscope, I use the following techniques:
- Stick to JRE Emulation classes as much as possible
- GWT core classes can be reasonably abstracted (Timer, Network requests, etc)
- Abstract away GWT Widgets where possible (MyMenuItem interface vs Menuitem)
- Isolate all JSNI code into a browser specific impl package
- Use a platform specific Factory/Toolkit to create implementation instances of various abstractions (in Chronoscope, this is called the View class)
Package layout
Here's the package layout I use:
- org.timepedia.chronoscope.client - all 'cloud safe' code goes here and in subpackages
- org.timepedia.chronoscope.browser - any class performing any operation that transitively requires gwt-user or JSNI goes here
- org.timepedia.chronoscope.java2d - Java2D cloud specific Canvas implementation
- org.timepedia.chronoscope.client.flash - Flash specific cloud code here
- org.timepedia.chronoscope.android - Android specific View/Canvas stuff here
org.timepedia.chronoscope.server - Servlet Chart Server stuff
Example Abstraction: Timers
Chronoscope does a lot of interpolated animations, and to do so, it needs the use of a timer. Usage of the GWT Timer class would not allow the code to run as an Applet or compile for Android, so instead, this is how timing is done in Chronoscope:
package org.timepedia.chronoscope.client.util;
/**
* Abstraction for running scheduled tasks, independent of JRE environment
*/
public interface PortableTimer {
public void cancelTimer();
public void schedule(int delayMillis);
public void scheduleRepeating(int periodMillis);
double getTime();
}
And of couse, the BrowserView factory class which returns instances that work in GWT:
/**
* Creates a PortableTimer based on GWT's Timer class.
*
* @param run
* @return
*/
public PortableTimer createTimer(final PortableTimerTask run) {
return new BrowserTimer() {
public void run() {
run.run(this);
}
public void cancelTimer() {
cancel();
}
public double getTime() {
return new Date().getTime();
}
};
}
I have created similar abstractions for menus, toolbars, and other things that Timepedia needs, and as I discover new widgets that are needed, I add 'cloud enabled' versions as I go along.
UI Widgets tend to have analogs on every platform that function slightly differently, but it's not hard to seek out minimalist lowest common denominator behavior.
Sometimes this approach fails
So far, I have discussed design techniques to make recompiling for the cloud relatively automatic and painless, but for reasons I cited in the beginning, as well as other fundamental differences, you cannot expect something to be write-once work everywhere. Here's some differences that will bit you:
- JDK1.5 vs JDK1.4 language features (mostly fixed by GWT 1.5)
- JRE Emul collections vs CLDC/J2ME
- Pointing device/events. Not every platform will have gamepad keys, support double-click, single click, drag, etc. The iPhone being the biggest example.
- Network security policies (no access, extremely slow access, same domain access, full access) Sometimes remedied by proxies, but usually a pain in the ass no matter what.
When abstraction fails...
Maintain two branches of your codebase. I do this for J2ME due to CLDC <-> JRE Emul conflicts. I also do it for UI event handling, as each application has to be tailored for the screen format and keyboard/input device of each platform. Years of mobile industry development have taught me the painful fallacy of trying to devise a general purpose app that is not device specific. Want the best user experience? You have to tailor for form-factor and input device.
A preprocessor can sometimes come to the rescue (Foo/*<Bar>*/ -> Foo<Bar> on 1.5+ platforms, Retroweaver can help smooth over 1.5->1.4 collection issues, and there are hacks/tools to inject 1.1 style collection interfaces into J2ME platforms that lack them, such as JDiet)
Future Directions
Both GWT and Android are moving towards compile time declarative UIs, it may be possible in the near future to create a unified declarative UI syntax that allows code generation for both GWT widget layouts and Android.
That's about all I can think of to say on the subject right now. GWT permits Cloud Computing, it was one of the reasons I chose it, and if you're interested in building components that can live within GWT and Android, it is certainly possible.
-Ray
p.s. some might technically quibble and say it's not true cloud computing because all of the tiers I consider are client-side platforms, but Chronoscope does run on the server too, and I can generate old Web 1.0 style image-map interfaces for navigation (JFreeChart can do this as well)
Posted by Timepedia at 3:47 PM 2 comments
Thursday, December 6, 2007
Editorial: Proof of why GWT Deferred Binding rocks
Soap Box On:
So, I admit it. I'm biased. I gave the presentation on Deferred Binding at the GWT Conference. It's hard explaining to people why this is such a powerful and needed feature in GWT. End users especially won't really be able to grok why, but I think Microsoft provides the best evidence in the form of Microsoft Volta, the GWT competitor from Microsoft that was supposed to blow GWT away.
Microsoft's test application certainly blows away my browser: http://labs.live.com/volta/samples/WordWorm.html
It made over 171 HTTP requests to load up all of its generated Javascript, over 2 megabytes of code, it took 20 seconds to startup, ran slow once it did, threw exceptions and sent me into the debugger, and when I looked at the code, I noticed that it had compatibility code for other browsers in my download, code chewing up space and network bandwidth that are useless to my Firefox instance.
I realize that this is a prototype, but come on. Microsoft should not be talking smack about GWT until they've got something to show that doesn't have so many easy to criticize flaws.
-Ray
Posted by Timepedia at 5:02 PM 2 comments