RSS

GWT- About Generators and Exporters

01 Oct

Why do at runtime what you can do at compile time?

orginally this article is written by Ray (Google developer)

If GWT had a mantra, this would be it.

Introduction

The GWT compiler deals with a closed world — no dynamic class loading, but it does permit deferment of binding decisions until compile time via a rule based mechanism that is part of the external GWT Module metadata. You can choose to replace a given type with other preauthored types, or, and here’s the important part: you can replace types with classes that are generated on the fly.

This is the compile time equivalent of what you would do at runtime with libraries like CGLIB or the JDK’s Proxy/Interceptor classes, and similar to Sun’s APT (Annotation Processing Tool), except that it’s all integrated into GWT and you don’t need to worry about running separate APT-passes.

The deferred binding mechanism is used heavily in GWT to replace standard implementations of browser widgets with quirky implementations for browsers with divergent behavior. GWT then compiles multiple permutations of your code base, running the binding rules separately for each browser. Thus, if you have rules to target 4 different browsers, you end up with GWT compiling 4 different code bases and producing 4 compiled outputs.

Why does it do this? Because the result is more optimal code. Otherwise, you would have to include all 4 Implementation subclasses in your main code base, and use runtime logic to dynamically call the appropriate target, e.g. “if(brokenDom) DOMImplIE.foo(), else… DOMImplStandard.foo()…” It shortens load time by only forcing your browser to load what is neccessary, and it shortens run time by removing another level of indirection. Finally, it permits the optimizer to actually inline the appropriate implementation directly into the call site.

Generators

GWT also uses compile time generation of Java code to implement many platform features. The most famous of course is the RPC/Serialization mechanism. Here, GWT takes an interface, such as MyServiceAsync, and generates an implementation of this class on the fly, which contains all of the logic needed to serialize non-primitive types, send them across the wire via XMLHttpRequest, invoke the RemoteService, deserialize the rule, and invoke the async handler. It is probably the most complex generator in GWT, but not the only one.

Internationalization and Localization are also handled by the generator mechanism. Instead of loading ResourceBundles at runtime, you instead pre-process them and turn them into an interface, with one method per property. GWT will then use a generator to fill in the implementation of this interface which returns the values that are in the property file when the corresponding method is invoked. And what if you don’t use some of the properties? The GWT compiler will remove any unused (uncalled) properties from the compiled application, and the number of HTTP requests is reduced because the locale data is bundled inline with your code.

Finally, truly the coolest and most innovative application of generators to date is the ImageBundle. In the world of 3D graphics programming, there is a technique called Texture Atlas, wherein you combine many textures into a single large texture, because on many graphics accelerators, changing pipeline state, such as binding a new texture before drawing geometry, is an expensive operation or may stall the pipeline. A program using texture atlases instead, binds one or more mondo-big textures, and simply uses texture coordinate manipulations to render portions of them as needed.

GWT 1.4 uses a similar technique to reduce load times and network traffic. With ImageBundle, like the I18N mechanism, you create an interface with a bunch of methods, one for each Image, as well as metadata annotations telling the GWT compiler which image file on disk you want returned by the method. Then, at compile time, the GWT compiler combines all of the images together into one large image file and generates an implementation class to return, essentially, the bounding box location of where each image is located within the overall atlas. Finally, when drawing the images, you just draw the same image (the large one) over and over, but use clipping rectangles to show only the part which corresponds to the image you need.

This reduces the number of HTTP requests drastically, speeds up startup time, and also centralizes media resources to a factory. The I18N and ImageBundle technique may even be combined to produce localized image bundles.

You’re starting to imagine the possibilities?

How about buttons rendered with drop-shadows? Rounded corners done at compile time? Object-relational mapping to Google Gears or serialized RowSets? Type-safe JSON wrappers?

The project that I have created as my first generator is one designed specifically to suit my needs. Chronoscope has a JavaScript API that allows pure JS developers to access functionality provided in GWT classes. The way it does this is by creating what I call “bridge” classes and methods in the top level browser namespace. These bridge classes contain the public names (non-obfuscated) of GWT methods that will be invoked by the Javascript-to-Java JSNI Mechanism.

The problem is, everytime I added a method to one of my GWT classes, I had to go write a bridge method for it, and as Chronoscope grew in size and complexity, I needed a more automatic, and safe, mechanism for exporting an external JS interface.

GWT Exporter

The first step is to decide what your generator is going to do. In my case, I want the generator to introspect one of my GWT classes, and generate an exported JS API with bridge methods.

What’s a bridge method?

Imagine you have the following GWT Class:

package org.foo.bar;

public class Util {
public static String doSomethingUseful(int x) {
return “Hello “+x;
}
}

And you want to allow JS users to call the doSomethingUseful() function?

Today, you would use JSNI to export ‘bridge methods’ like so:

package org.foo.bar;
public class Util {
public static String doSomethingUseful(int x) {
return “Hello “+x;
}
public native void export() /*-{
$wnd.doSomethingUseful = function(x) {
@org.foo.bar.Util::doSomethingUseful(I)(x);
}-*/;
}
}

$wnd stands for the top-level window object, and by assigning to its doSomethingUseful property, you ensure GWT won’t obfuscate it. The JSNI call to Util.doSomethingUseful will be obfuscated however, thus the bridge method is neccessary to export the obfuscated symbol.

It gets more complicated if you want to bridge a non-static function, but here’s an example:

public class Employee {
private String firstName, lastName;
public Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}public String getFirstName() {
return firstName;
}public String getLastName() {
return getLastName;
}public static Employee createEmployee(String firstName,
String lastName) {
return new Employee(firstName, lastName);
}public native void export() /*-{
$wnd.Employee = function(firstName, lastName) {
// call factory method and store GWT reference
this.instance =
@org.foo.bar.Employee::createEmployee(Ljava/lang/String;
Ljava/lang/String)(firstName, lastName);
}

var _=$wnd.Employee.prototype;
_.getFirstName = function() {
this.instance.@org.foo.bar.Employee::getFirstName()();
}
_.getLastName = function() {
this.instance.@org.foo.bar.Employee::getLastName()();
}
}-*/;
}

Which you may use as

x = new Employee(‘Ray’, ‘Cromwell’);
alert(x.getFirstName());

As you can see, manual bridging gets tedious!

Generators to the rescue!
The first step in implementing a generator is to decide on a type that will be used to trigger the generator. So let’s introduce a new marker interface called ‘Exportable’. GWT also want the generated class to implement the Exporter interface, primarily, the export() method.

Here they are:

package org.timepedia.exporter.client.Exportable;
public interface Exportable {
}public interface Exporter {
public void export();
}

Simple eh? Next we’ll add the following line to our GwtExporter.gwt.xml module file:

<generate-with
class=”org.timepedia.exporter.rebind.ExporterGenerator”>
<when-type-assignable
class=”org.timepedia.exporter.client.Exportable”/>
</generate-with>

What does this mean?

It means that when GWT.create() is invoked with a type that can be assigned to an Exportable, invoke the generator. That is, we want to write

public class Employee implements Exportable { … }

Exporter x=(Exporter)GWT.create(Employee.class);
x.export();

and have it invoke the generator.

Specifying what gets exported

Next we have to decide on the rules for exporting. Which methods of an Exportable get exported? How do we control the generated JS namespace? etc. For now, let’s settle on the following logic — a method is exportable IF and ONLY IF:

  1. The class enclosing the method implements Exportable
  2. Metadata has determined it’s ok to export (more on this later)

Also, we need error checking. It is an error to export a method if any parameter or return type is not one of:

  1. a primitive type (int, float, etc)
  2. another Exportable
  3. an immutable JRE type (String, Integer, Double, etc)
  4. JavaScriptObject

Metadata

GWT has its own form of annotations similar to JavaDoc/XDocLet tags. We will use this to control export policy. We will support two forms of export policy:

  1. White List
    1. By default, nothing exported.
    2. Each method to be exported must have a “@gwt.export” metadata annotation
  2. Black List
    1. Place “@gwt.export” on class itself (in JavaDoc for class)
    2. By default, all public methods exported
    3. Each method to be removed from export consideration tagged with “@gwt.noexport”

Finally, by default, the GWT Class’s package will be used as the JS’s namespace, (e.g. new $wnd.org.foo.bar.Employee). If you wish to use another package for the JS export, place “@gwt.exportPackage [package1.package2...]” on the class JavaDoc.

As an example:

package org.foo.bar;
/**
* @gwt.export
* @gwt.exportPackage examples
*/
public class Employee implements Exportable {
private String firstName, lastName;
public Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}public String getFirstName() {
return firstName;
}
/**
* @gwt.noexport
*/
public String getLastName() {
return getLastName;
}

and uses black-list policy to export getFirstName() but supress the export of getLastName(); Using white-list policy, you would write:

package org.foo.bar;
/**
* @gwt.exportPackage examples
*/
public class Employee implements Exportable {
private String firstName, lastName;
/**
* @gwt.export
*/
public Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
/**
* @gwt.export
*/
public String getFirstName() {
return firstName;
}public String getLastName() {
return getLastName;
}

to achieve the same effect.

At this time, GWT are not considering function overloading.

About these ads
 
2 Comments

Posted by on October 1, 2008 in GWT/ JSNI / COMPILER

 

Tags: , , , , , , ,

2 responses to “GWT- About Generators and Exporters

  1. ImmetsWen

    May 20, 2009 at 8:17 pm

    Outstanding info!! Will definitely come back again.

     
  2. tamgo

    August 3, 2009 at 6:52 am

    Excellent post. I really needed info on the topic, and did not find a lot in the official docs. Thanks for taking the time to write this up!

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: