Tuesday, June 17, 2008

GWT and Metawidget Part 3: Binding

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically bindings are now set using .addWidgetProcessor. Please download the latest documentation from http://metawidget.org

The latest release of Metawidget adds supports for Google Web Toolkit (GWT) 1.5. This series of blogs explores the challenges that had to be overcome to acheive this.

Two Way Binding

Almost all Web frameworks support some form of automatic two-way binding between Web pages and domain objects. Some supply sophisticated expression languages (Java Server Faces), others straight property name mapping (Struts), but most supply something.

Except GWT.

To be fair, Google are planning to add this feature in an upcoming release - but it's not there in 1.5 just yet. One of the main stumbling blocks is that most binding implementations use reflection, and JavaScript doesn't support reflection.

As an interim solution, Metawidget supplies SimpleBinding. Metawidget's binding implementations are pluggable, so we can swap in 'real' GWT binding one day, but for now SimpleBinding gets you most of the way: a simple two-way binding, including converters.

But! How do we solve the reflection problem?

As with the last blog entry, Generators are our friend. SimpleBinding uses SimpleBindingAdapters, and comes with a SimpleBindingAdapterGenerator that can generate SimpleBindingAdapters automatically for your business classes.

A SimpleBindingAdapter is capable of 'reflecting' every property (and even sub-property) of a business class by pre-generating code that automatically traverses getters and calls setters. In effect, it statically generates codes that looks like this:

if ( "address".equals( property[0] )) {
   Address address = contact.getAddress();
   if ( property.length == 1 ) return address;

   // Address properties

   if ( "street".equals( property[1] )) {
      if ( property.length > 2 )
         throw new RuntimeException( "Cannot reflect into property 'street." + property[2] + "'" );
      return address.getStreet();

To stop the tree of possible properties getting too large, it restricts itself to only classes within the same package or sub-package.

To use SimpleBinding in your code, add the following to your application-name.gwt.xml file:

<generate-with class="org.metawidget.gwt.generator.binding.simple.SimpleBindingAdapterGenerator">
   <when-type-assignable class="org.metawidget.example.shared.addressbook.model.Contact"/>

...and in your application code add...

metawidget.setBindingClass( SimpleBinding.class );
SimpleBindingAdapter<Contact> adapter = (SimpleBindingAdapter<Contact>) GWT.create( Contact.class );
SimpleBinding.registerAdapter( Contact.class, adapter );

How does this work in practice? See for yourself! The Metawidget download includes a pre-built sample application, addressbook-gwt.war, that showcases a GwtMetawidget using two-way bindings in the UI. It's covered in detail in the reference guide.