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
Monday, December 10, 2007
Deferred Binding slides online
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
Wednesday, November 28, 2007
Chronoscope in Swing and Servlet environments
After pushing out the open source release of Chronoscope, I finally went back to work on Timelord, which is our all-purpose GWT application for exploring our time series database, data mining, annotating visualizations, and tons more. Hopefully I can get it patched up to show something at the Pearson GWT Conference
Anyway, when data mining, Timelord shows icons to the user of interesting patterns which are server-side generated Chronoscope charts. The intense refactoring for the open source release broke the server-side implementation, so I spent most of yesterday and today, re-merging in and fixing the Java2D Canvas implementation layer.
I just finished getting a prototype up and running, and decided to put up a demo of Chronoscope running as an Applet (it's alittle beefy to download at the moment, I was able to pack200 it down to 120kbytes, but haven't gotten the webserver reconfigured yet to serve pack200 files.) This brings to 4 the number of environments the Chronoscope codebase can exist in: Browser-based Javascript, Swing desktop applications and Applets, Servlet-generators, and the Google Android phone SDK.
After the GWT Conference, I'll push to get a Flash implementation done, in which case, you'll have the choice of generating charts via Canvas, Applet, Flash, or Server. That will leave only J2ME as the final target to crack.
I'll commit the Java2D layer after the conference and things settle down. We've been working on Timepedia slowly for years now, and I'm anxious to start showing some of the real site, so I want to get Chronoscope development stabilized soon. (code-freeze)
-Ray
Posted by
Timepedia
at
4:04 PM
0
comments
Saturday, November 24, 2007
Chronoscope responsiveness increased
Yesterday, we received reports of slowness in Chronoscope on low end PCs. Admittedly, we didn't do testing on any PCs from yesteryear, but we will do so in the future.
In any case, we made some changes to Chronoscope that should make it feel faster on low end PCs:
- Animation was uninterruptible and fixed at 8 frames. On a slower PC that takes longer than 300ms or so to render these frames, there is a distinct feeling of lag when you press a key. Animations in progress are now interruptible.
- Key frame interpolation was frame count based instead of wall clock based. Interpolation frames are now parameterized based on time, and maximum animation time is capped at 300ms. Faster computers just generate a higher framerate for smoother, less jumpy, rendering.
- Lowered the resolution of the dataset further when animating. Future versions will auto-adapt this to platform speed.
- The final 'full resolution' dataset used to be displayed, no matter what, after an animation sequence finished (between keypresses). Now, it is delayed a short time until the user stops navigating.
The changed version has been deployed to timepedia.org for testing, but not committed to the source code repository yet. Check it out and let us know if you still have performance problems.
The only known issue at the moment is that mouse dragging on some platforms is jerky and laggy. We are looking into fixing this next.
-Timepedia Team
Posted by
Timepedia
at
12:08 AM
2
comments
Tuesday, November 20, 2007
GWT and Android: A marriage made in heaven?
The Google Web Toolkit has many awesome advantages for developing AJAX applications: Reuse of your favorite toolchain features (IDE, codeassist, debugger, refactor, build, etc), as well as producing much more compact Javascript code than any JS compiler/obfuscator can ever hope for. But one feature that initially caught our eye was the ability to reuse code in multiple environments.
Chronoscope started as two separate projects. The first, a prototype server-side renderer for Timepedia using the very capable JFreeChart. The second, a pure-JS canvas version written to test the ability to do AJAX charts without Flash. The JFreeChart version then became the version which would be used for static chart icons, sparklines, fallback for older browsers, and export to PDF/SVG.
Immediately, we encountered the problem that the two code bases had different features, different rendering style, which required laborious coding to keep them matched as close as possible. Over time, as the feature sets evolved, it became harder and harder to keep the JS client charts and server-side charts in sync.
Then Google released GWT and it immediately offered a solution: Write one chart library, in Java, and deploy to Servlet, Applet, Browser JS, and maybe J2ME and Flash. It was an enormous promise, and we initially adopted GWT for this purpose without realizing the other tremendous benefits that GWT provides for developing large JS codebases.
Early on, we produced prototypes of GWT Chronoscope running in servlet and applet environments, but with the release of the Android SDK, the initial promise has been fulfilled: GWT code running in a mobile environment.
The following is a screencast demo showing Chronoscope, with no changes to the core codebase running in the Android SDK emulator natively (not in the web browser).
It required about 8 hours to get this working. Most of the time was spent finding the Android equivalents of Java2D calls, and writing 6 Java classes (the Chronoscope Canvas abstraction layered over Android Graphics API)
At this point, we've only touched the tip of the iceberg. Future enhancements in GWT may allow this portability to go even further, such as compiling to ActionScript.
If your attending the GWT Conference on December 3-6, feel free to track down Ray Cromwell or Shawn O'Connor for a live demo.
p.s. There are no ticks or axis labels being shown in the sceencast to maximize screen real estate as well as performance. Android SDK text rendering is a drag on performance at the moment.
Posted by
Timepedia
at
11:41 AM
5
comments