Wednesday, March 12, 2008

GWT: The Road to 1.5, Linkers

The Google Web Toolkit compiler is a marvelous tool for turning Java code into highly optimized Javascript, but prior to GWT 1.5, there was no mechanism for developers to customize the packaging of the compiler output, or the bootstrap of the application.

C developers are used to the ability to override the entry point glue code ('start' method in crt1.o for example), or to package statically, dynamically, etc, so why not provide this capability for GWT developers as well?

This will allow GWT compiled code to packaged and bootstrapped into environments like Flash/AIR Apollo, Gears, Gadget Containers, and the example I'm going to show today: Android Offline/Online Hybrid applications.

What's a Syndroid


As part of my ongoing prototyping in Syndroid, I stumbled onto the idea of a hybrid Android application. One where some of the UI can be rendered via GWT in the embedded WebKit, and the other part, running as native Java/Dalvik code accessing Android APIs.

For example, you may wish to develop a Gadget with GWT, that runs in several containers, but when it runs on Android, it has additional access to information like Contacts, Location, etc. How can GWT code call native Android functions?

Generators Again? The Android Native Interface


Before showing you the magic sauce that permits this, let's look at an example API of how we might like this to work. This will all seem familiar to those who use GWT RPC. First, let's create an interface that will trigger a GWT Generator.

package org.timepedia.syndroid.client.android;
public interface AndroidNativeInterface {}

and a sample interface exposing a method to retrieve our GPS coordinates

package org.timepedia.syndroid.client.android;
@ImplementedBy(LocationServiceImpl.class)
public interface LocationService extends AndroidNativeInterface {
String getLocation();
}

next, Android implementation code (*warning, code may not work, I just typed it into the blog without testing)

package org.timepedia.syndroid.android;
public class LocationServiceImpl implements LocationService {
public Location getLocation() {
LocationManager lm = (LocationManager)
Context.getSystemService(Context.LOCATION_SERVICE);
return locationManager.getCurrentLocation("gps").toString();
}
}

finally, some GWT code that uses it

LocationService lService = (LocationService)GWT.create(LocationService.class);
Window.alert("My location is: "+lService.getLocation());

Great, but how does it work, and how do Linkers fit in?


There are a few ways that RPC calls between GWT and Android could work. For example, a traditional XHR request could be made to a HTTP service running on Android. But this would require the API to be asynchronous and have a high overhead.

It turns out, that the native WebKit that comes with Android has the capability to extend the native Javascript APIs available via Java. For example,

WebView wv=new WebView(this);
wv.getSettings().setJavaScriptEnabled(true);
wv.addJavascriptInterface(new LocationServiceImpl(), "locationService");

This binds our Android LocationServiceImpl instance to a globally scoped JavaScript object called 'locationService', allowing Javascript code to invoke the locationService.getLocation() method.

We now have two problems to solve, first, generate GWT code in a Generator that invokes this $wnd.locationService.getLocation() global object, and secondly, to generate Android bootstrap code (shown above), an AndroidManifest, and package up everything into a APK file that can be installed on a phone. The latter problem is what Linkers help solve.

The Generator's Job


I won't go into detail about how to implement the Generator, as it's been covered before in this blog, the Generator essentially creates a LocationServiceImpl class, with JSNI method getLocation(), which calls $wnd.locationService.getLocation()

The Linker's Job


The Linker has the bulk of the grunt work here. Here's what it must accomplish:
1. Generate Bootstrap Android application
2. For each generated AndroidNativeInterface, add Javascript bindings
3. Place all generated resources in proper Android asset layout
4. Create Android Manifest file
5. Add extra code to load the initial WebView start page with GWT app
6. Invoke Android tools to compile Dalvik code, and package APK files.


The Linker and LinkerContext interface in GWT 1.5 allow one to specify in a module file, which sets of Linkers are run. GWT 1.5 provides some standard ones like the IFrameLinker and XSLinker, which generate the ordinary selection scripts we see in 1.4. The LinkerContext interface provides a mechanism to discover the outputs from the compiler pass, and to emit new ones.

I'll have to end this installment here, because it is getting long and I need to get back to work, in the next installment, I'll show some actual Linker code for an AndroidGadgetLinker.

Friday, March 7, 2008

GWT:The Road To 1.5, Part 1

The next version of Google Web Toolkit is almost upon us, GWT 1.5, but don't take the minor version bump as an indication of how much it's improved, GWT 1.5 has so many awesome improvements, it would be more proper to call it GWT 2.0. That's one reason why it's been almost a year since the last release.

To celebrate the release, I will be writing a series of brief articles on each of the many improvements of 1.5, hopefully with sample code demonstrations.

Zero Overhead Javascript Interop
One of the cool things about GWT has always been the JSNI concept, or 'Javascript Native Interface', that neatly parallels JNI in ordinary Java, except the 'native' implementation is Javascript.

One of the most common uses of JSNI is to produce wrappers for third party Javascript libraries like Scriptaculous, Dojo, ExtJS, etc. Unfortunately, these wrappers are somewhat expensive prior to 1.5

Common Wrap Patterns
There are two common ways people wrap third party Javascript libraries: Encapsulation of JavaScriptObject ('JSO'), or extension of JavaScriptObject. Prior to 1.5, subclassing of JSO worked, but was not officially supported.

Regardless of which technique is chosen, the wrappers work by providing access to Javascript object properties via JSNI getters and setters, and methods via JSNI methods that delegate to Javascript methods.

Unfortunately, delegation getter methods or other instance methods, were not optimized away or inlined by the GWT compiler. Let's look at an example.


public class TestWrapperEncapsulation {
private final JavaScriptObject jso;
public TestWrapperEncapsulation(JavaScriptObject jso) {
this.jso = jso;
}
public native String getFoo() /*-{
return this.@TestWrapperEncapsulation::jso.foo;
}-*/;
public static native JavaScriptObject makeObject() /*-{
return {foo: 'Hello World'}
}-*/;
public static TestWrapperEncapsulation create() {
return new TestWrapperEncapsulation(makeObject());
}
}


Given the above wrapper class, if you were to write:


Window.alert(TestWrapperEncapsulation.create().getFoo());


You would not end up with the desired optimal JavaScriptCode, e.g.

$wnd.alert(TestWrapperEncapsulation.create().jso.foo)


rather, this


$alert(this$static, $getFoo(create()));

function $alert(this$static, foo){
$wnd.alert(foo);
}

function $TestWrapperEncapsulation(this$static, jso){
this$static.jso = jso;
return this$static;
}

function $getFoo(this$static){
return this$static.jso.foo;
}

function create(){
return $TestWrapperEncapsulation(new TestWrapperEncapsulation(), makeObject());
}

function makeObject(){
return {'foo':'Hello World'};
}


Now let's take a look at GWT1.5's output:

$wnd.alert($TestWrapperEncapsulation(new TestWrapperEncapsulation(), {foo:'Hello World'}).jso.foo);

function $TestWrapperEncapsulation(this$static, jso){
this$static.jso = jso;
return this$static;
}


That's a massive reduction, but still not good enough. There is in fact, no reason to have a wrapper encapsulate the JSO. Instead, with GWT 1.5, we can subclass the JSO and pretend that our Java methods exist on the underlying JSO.


alert(TestWrapper.makeWrapper().getFoo());

public class TestWrapper extends JavaScriptObject {
protected TestWrapper() { }
public final native String getFoo() /*-{
return this.foo;
}-*/;

public static native TestWrapper makeWrapper() /*-{
return { foo: "Hello World" }
}-*/;
}

and what does the 1.5 compiler produce?

$wnd.alert({foo:'Hello World'}.foo);


Perfect! Absolutely zero overhead.