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)

2 comments:

Deva said...

Ray, I was the one who asked about GWT development for Android - Thanks for the very informative article - I will be checking out your blog in a regular basis.

Oh and Thanks for Deferred Binding slides too :) --Deva.

Anonymous said...

(法新社倫敦四日電) 英國情色大亨芮孟的公司a片昨天a片下載說,芮孟日前去世,享壽八十二歲;這位身價上億的房地產開發商,曾經在倫敦av女優推出第一情色視訊成人影片脫衣舞表演。色情
av情色電影
情色
芮孟的av女優財產av估計達六億五千萬英鎊(台幣將近四百億),由日本av於他名下事業大多分布在倫敦夜生活區蘇活區,因色情影片成人擁有「蘇活之王」的稱號。成人網站


他的公司「保羅芮情色孟集團」旗下發行部落格多種情色雜誌,包成人網站括「Razzle」、「男性世界」以及「Ma部落格yfair」。


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


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

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