At my “day job” at Manheim Auctions we have been using GWT for a while now on “real world” projects. Along the way we have built a lot of tooling for GWT and we are now releasing a big chunk of it as Gwittir (like Glitter with a lisp). There are a number of tools included here, but the real core of it is made up of “lite” version of things you are accustomed to the the larger Java world. Here we will take a look at Beans Binding, and Animation to create a Flickr browser (running example at the link).
I kind of joke that this project is a roll up of a lot of the open source work I have done for the last couple years as it uses ROME and the MediaRSS module and is built with gwt-maven which I have talked about in this space before. You can grab the source for the example here. A long look at the code after the jump.
Most of you, I am sure, are at least somewhat familiar with GWT and what it is. One of the problems with GWT, though, is there are no clear ways to accomplish certain goals. Over the last year at Manheim we have been doing a good bit of GWT work, so we had to scratch our own itch in terms of some tooling. Hence, Gwittir.
Much like GWT starts with the basic Java cross-compiler and builds on top of it to provide a full toolkit, Gwittir starts with a Beans-style introspection system, then includes a lot of things built on top of it. In this post I am going to demonstrate using the Data Binding system and our Animation system. Much like our Introspection system is Beans-lite, these systems are “Lite” versions JSR-295 and Swing Timing Framework. Here I am going to build a Flickr browser with a proxy RPC service.
The first thing we need to do is create our “Bindable” data bean. The Bindable interface extends two others: SourcesPropertyChangeEvents and Introspectable. The first is our marker interface for PropertyChangeSupport and the later tells the compiler plugin to build runtime Introspection data for the bean. Since we are going to use this as a RPC return value, it will also implement IsSerializable.
public class Photo implements Bindable, IsSerializable {
private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
this);
private String thumbnail;
private String standard;
private String title;
public void addPropertyChangeListener(PropertyChangeListener l) {
this.propertyChangeSupport.addPropertyChangeListener(l);
}
public void addPropertyChangeListener(
String propertyName, PropertyChangeListener l) {
this.propertyChangeSupport.addPropertyChangeListener(propertyName, l);
}
public PropertyChangeListener[] getPropertyChangeListeners() {
return this.propertyChangeSupport.getPropertyChangeListeners();
}
public void removePropertyChangeListener(PropertyChangeListener l) {
this.propertyChangeSupport.removePropertyChangeListener(l);
}
public void removePropertyChangeListener(
String propertyName, PropertyChangeListener l) {
this.propertyChangeSupport.removePropertyChangeListener(
propertyName, l);
}
public String getThumbnail() {
return thumbnail;
}
public void setThumbnail(String thumbnail) {
this.propertyChangeSupport.firePropertyChange(
"thumbnail", this.thumbnail, this.thumbnail = thumbnail);
}
public String getStandard() {
return standard;
}
public void setStandard(String standard) {
this.propertyChangeSupport.firePropertyChange(
"standard", this.standard, this.standard = standard);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.propertyChangeSupport.firePropertyChange(
"title", this.title, this.title = title);
}
}
Next we will build our remote service that returns photos based on a tags query. For this I am using ROME, ROME-Fetcher and the MediaRSS module. I make a call to the Flickr feed service and turn the media:* tags into Photo objects:
public class FlickrServiceServlet extends RemoteServiceServlet implements FlickrService{
private FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance();
private FeedFetcher feedFetcher = new HttpURLFeedFetcher(feedInfoCache);
public List getPhotos(String[] tags) {
String url = "http://api.flickr.com/services/feeds/photos_public.gne?lang=en-us&format=rss2&";
if( tags != null ){
url+="tags=";
for( int i=0; i < tags.length; i++ ){
url+=tags[i].trim()+",";
}
url+="&";
}
try{
SyndFeed feed = this.feedFetcher.retrieveFeed(new URL( url ) );
List photos = new ArrayList();
for( SyndEntry entry : (List<SyndEntry>) feed.getEntries() ){
Photo photo = new Photo();
photo.setTitle(entry.getTitle());
MediaModule media = (MediaModule) entry.getModule(MediaModule.URI);
String originalUrl = media.getMetadata().getThumbnail()[0].getUrl().toString();
String baseUrl = originalUrl.substring(0, originalUrl.length() -6);
photo.setThumbnail(baseUrl+"_s.jpg");
photo.setStandard(baseUrl+".jpg");
photos.add( photo );
}
return photos;
} catch (IOException e){
throw new RuntimeException(e);
} catch( FeedException e){
throw new RuntimeException(e);
} catch( FetcherException e){
throw new RuntimeException(e);
}
}
}
That is pretty easy to. The next “prefab” step is to create another Bindable bean that encapsulates this service. This will simply watch for changes to the “tags” property and refresh the “photos” property asynchronously. This one doesn’t have to be serializable, since it is a client-side class.
public class FlickrSearchBean implements Bindable {
private static final FlickrServiceAsync SERVICE = (FlickrServiceAsync) GWT.create( FlickrService.class );
static {
((ServiceDefTarget) SERVICE).setServiceEntryPoint( GWT.getModuleBaseURL()+"FlickrService");
}
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private String[] tags;
private List photos;
private AsyncCallback callback = new AsyncCallback(){
public void onFailure(Throwable t) {
throw new RuntimeException( t );
}
public void onSuccess(Object result) {
setPhotos( (List) result );
}
};
public FlickrSearchBean(){
SERVICE.getPhotos( null, callback);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
this.propertyChangeSupport.addPropertyChangeListener(l);
}
public void addPropertyChangeListener(
String propertyName, PropertyChangeListener l) {
this.propertyChangeSupport.addPropertyChangeListener(propertyName, l);
}
public PropertyChangeListener[] getPropertyChangeListeners() {
return this.propertyChangeSupport.getPropertyChangeListeners();
}
public void removePropertyChangeListener(PropertyChangeListener l) {
this.propertyChangeSupport.removePropertyChangeListener(l);
}
public void removePropertyChangeListener(
String propertyName, PropertyChangeListener l) {
this.propertyChangeSupport.removePropertyChangeListener(
propertyName, l);
}
public String[] getTags() {
return tags;
}
public void setTags(String[] tags) {
this.propertyChangeSupport.firePropertyChange("tags", this.tags, this.tags = tags);
SERVICE.getPhotos(tags, callback);
}
public List getPhotos() {
return photos;
}
public void setPhotos(List photos) {
this.propertyChangeSupport.firePropertyChange("photos", this.photos, this.photos = photos);
}
OK. The Beginnings are out of the way. Now lets look at building our UI and creating the bindings. I’m going to go through the EntryPoint code bit by bit so I can talk about it a little. First we create the Widgets we need. Here we are using Gwittir widgets and GWT panels. The Gwittir Widgets implement the BoundWidget interface, which is Bindable, but also exposes some standard methods. We will talk about these in just a moment. First lets look at the special widgets we are using that aren’t part of the core GWT.
public class FlickrEntryPoint implements EntryPoint {
public void onModuleLoad() {
TextBox box = new TextBox(false);
Label label = new Label("Comma separated tags:");
HorizontalPanel tags = new HorizontalPanel();
tags.add( label );
tags.add( box );
ReflectedImageGroup group = new ReflectedImageGroup(75, 75, .2, .5);
SoftScrollArea ssa = new SoftScrollArea();
ssa.setWidth("800px");
ssa.setHeight("150px");
ssa.setWidget( group );
ssa.addMouseListener( ssa.MOUSE_MOVE_SCROLL_LISTENER );
Image larger = new Image();
VerticalPanel vp = new VerticalPanel();
vp.add( tags );
vp.add( ssa );
vp.add( larger );
vp.setHorizontalAlignment( HasHorizontalAlignment.ALIGN_CENTER );
vp.setWidth("100%");
RootPanel.get().add(vp);
The ReflectedImageGroup is what gives us the tray of reflected images. Because of problems with cross-browser support, you have to give it dimensions when you build it. Those numbers in the constructor are “width, height, height of reflection, beginOpacityOfReflection.” Next we create the SoftScrollArea. This is a positioned element inside an overflow:hidden element, so we have to give it a known size for it to work right. The MOUSE_MOVE_SCROLL_LISTENER is a built in listener that does a Sinoidal scroll across the area based on mouse position. Finally we create the larger version of the image, put the whole thing in a VerticalPanel and place it in the Root.
The next feature demonstrated is the Renderer. All BoundWidgets support a Renderer. These allow you to get around what I consider the annoying bit of Swing where .toString() is used to render an object into whatever the widget needs to draw. Here I am going to replace the default ToStringRenderer with Renderers that will display the thumnail in the ReflectedImageGroup and the larger version in the Image instance:
group.setRenderer(
new Renderer() {
public Object render(Object o) {
return ((Photo) o).getThumbnail();
}
});
larger.setRenderer(
new Renderer() {
public Object render(Object o) {
return ((Photo) o).getStandard();
}
});
This is doing a String adaptation, but it doesn’t have to be a Stirng, just whatever the BoundWidgets “value” property wants to support. You will note there is a lot of use of Object here. This is a failing of Gwittir right now, but we are waiting on Generics support in GWT to clean up the type safety on these.
Next we are going to set up bindings. Bindings have several features. This example doesn’t cover validation, but you can see Converters. Converters allow you to adapt values between two properties that are of dissimilar types. Here we have the String value of the TextBox and the String[] value of the tags on the FlickrSearchBean.
FlickrSearchBean search = new FlickrSearchBean();
Binding images = new Binding(group, "value", search, "photos");
images.getChildren().add(
new Binding(
box, "value",
new Converter() {
public Object convert(Object original) {
if (original == null) {
return original;
} else {
return original.toString().split(",");
}
}
}, search, "tags",
new Converter() {
public Object convert(Object original) {
if (original == null) {
return original;
} else {
String[] strings = (String[]) original;
StringBuffer ret = new StringBuffer();
for (int i = 0; i < strings.length; i++) {
ret.append(strings[i]);
if (i < (strings.length - 1)) {
ret.append(",");
}
}
return ret.toString();
}
}
}));
At the top of this, you see the simplest form of a Binding. We are binding the “value” property of the ReflectedImageGroup to the “photos” property on the FlickrSearchBean. A lot of times this will be all you need, but there is a lot more there. Below that we add a child binding to that one. Children can be added and they will cascade from their parent. Here we create a binding with two converters. On the TextBox we use .split() to turn the String into Stirng[] and on the tags property we go the other way, building a comma delimited String value. This lets us bind the dissimilar types. There is one more step in the binding process which we will get to after the next listing. Fist, though, lets look at using the PropertyAnimator.
final OpacityWrapper largerOpacity = new OpacityWrapper( larger );
largerOpacity.setOpacity(new Double(0.0));
final PropertyAnimator animator = new PropertyAnimator( largerOpacity, "opacity",
new Double(0.0), new Double(1.0),
MutationStrategy.DOUBLE_CUBIC, 1000 );
group.addPropertyChangeListener("selected", new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent arg0) {
largerOpacity.setOpacity(new Double(0.0));
}
});
larger.addLoadListener( new LoadListener(){
public void onError(Widget arg0) {
largerOpacity.setOpacity( new Double(0.0) );
}
public void onLoad(Widget arg0) {
animator.start();
}
});
Since UIObjects in GWT don’t support Opacity (because it is a cross-browser issue) we are creating an OpacityWrapper around the Image object. The Gwittir Opacity wrapper works in all the browsers, adapting the Opacity to the proper DirectX Filter in MSIE. First we add a change listener on “selected” on the ReflectedImageGroup to hide the image while we get the next one. Then on the Image class we add a LoadListener that invokes the PropertyAnimator.
Here we are creating an animator with a target object, the property to change, the start value, the end value, a MutationStrategy implementation, and the number of millisections the animation to take to complete. There are several default implementations buildled. Here I am using a cubit transform on a Double value.
The final step is to initialize the values and perform the bind.
images.setLeft();
images.getChildren().add( new Binding(larger, "value", group, "selected") );
images.bind();
The setLeft() method tells the Binding to take all the values from the “left” objects, the first one in the constructor and set their values to those of the “right” objects. The convention we use is the “left” object is always the view for standard stuff, so .setLeft() is usually called. There is a .setRight() method available, however. Since the Image can’t have a “null” as its value, we wait to add it to the parent Binding until after setLeft. It will get changes from the “selected” property of the ReflectedImageGroup, but these will won’t be nulls when a selection is made. Finally we call bind.
In a larger application, you will also want to use .unbind(). This cleans up all the listeners on all the objects that the Binding has created, simplifying your cleanup code. In this one “screen” state application, we don’t really need that. More information about how to use a BindingAction for lifecycle management is available on the Wiki.
Hopefully this gives you a good taste of development with Gwittir. There are a lot more features which you can read about on the wiki, and a few big ones that we haven’t finished fully documenting. I hope you find this useful.



Great article - thanks for the code postings, those are always helpful. I wonder though - is it worth it to roll the bean binding yourself, or would it be better long-term to bundle Spring in?
Well, you can't really bundle Spring into your GWT applications. GWT isn't a large enough subset of Java to support Spring in the client.
Right now we are planning to support a declarative binding system when the declarative UI stuff for GWT 1.5 ships, which will simplify the code more, and be more "Spring-y". Honestly, it is entirely possible GWT will gain some kind of data binding on its own, too. I know there is discussion of it on the mailing lists. For right now, though, this was our best option for what we needed to get done.
Great article. Really interesting stuff.
Data binding is really a missing feature in GWT. This will help a lot.
Im still missing some clues to get a proper GWT MVC application in place.
How does DataBinding fit in the model ?
Can it help building coarse grain components (Composite) ?
Do we need a complementary MVC framework, or did you manage to organize large scale application with just Javabeans properties and Listener ?
As for Spring and IOC, do you think GWTx could be used for a basic declarative IOC mechanism ?
Thx
Well, Gwittir includes our MVC framework as well. It is built on top of JavaBeans stuff, but there is a good bit more there.
In terms of RPC, the way we work is we return RPC object that are Bindable then we bind those to our UI components. You can build large composite bindings as well (you can see the Wiki for more info on that). We are hoping to have a declarative binding format that works with the GWT declarative UI soon.
I will post some more stuff on the Gwittir libraries when I get a chance.