Easy Property Binding and Aync Callbacks in GWT

This is a little technique I came up with a few days ago that makes Async Callbacks a lot easier. It also adds something a lot like Property Bindings to GWT with very little work.

public interface PropertyChangeListener {
    void propertyChanged(SourcesPropertyChangeEvents source, String propertyName, Object oldValue, Object newValue);
}

public interface SourcesPropertyChangeEvents {
    void addPropertyChangeListener(PropertyChangeListener listener);

    void addPropertyChangeListener(String propertyName, PropertyChangeListener listener);

    void removePropertyChangeListener(PropertyChangeListener listener);

    void removePropertyChangeListener(String propertyName, PropertyChangeListener listener);

}

public class PropertyChangeListenerCollection extends AbstractCollection<PropertyChangeListener> {
    private final Map<String, Set<PropertyChangeListener>> listeners = new HashMap<String, Set<PropertyChangeListener>>();

    private Set<PropertyChangeListener> getListenersForProperty(final String name) {
        Set<PropertyChangeListener> set = listeners.get(name);

        if(set == null) {
                set = new HashSet<PropertyChangeListener>();
                listeners.put(name, set);
        }

        return set;
    }

    // this is a simple utility method that avoids duplicate copies of the same
    // PropertyChangeListener
    private Set<PropertyChangeListener> getAllListeners() {
        final Set<PropertyChangeListener> all = new HashSet<PropertyChangeListener>();

        for(final Set<PropertyChangeListener> set : listeners.values()) {
            all.addAll(set);
        }

        return all;
    }

    public void add(final PropertyChangeListener listener) {
        add(null, listener);
    }

    public void add(final String property, final PropertyChangeListener listener) {
        if(listener != null) {
            getListenersForProperty(property).add(listener);
        }
    }

    public void remove(final PropertyChangeListener listener) {
        if(listener != null) {
            for(final Set<PropertyChangeListener> set : listeners.values()) {
                set.remove(listener);
            }
        }
    }

    public void remove(final String property, final PropertyChangeListener listener) {
        if(listener != null) {
            getListenersForProperty(property).remove(listener);
        }
    }

    // although unused I've provided a simple implementation of the size method
    public int size() {
        return getAllListeners().size();
    }

    public Iterator<PropertyChangeListener> iterator() {
        return getAllListeners().iterator();
    }

    public void firePropertyChangeEvent(final SourcesPropertyChangeEvents source, final String name,
            final Object oldValue, final Object newValue) {

        final Set<PropertyChangeListener> propertyListeners = new HashSet<PropertyChangeListener>();
        propertyListeners.addAll(getListenersForProperty(null));
        propertyListeners.addAll(getListenersForProperty(name));

        for(final PropertyChangeListener l : propertyListeners) {
            l.propertyChanged(source, name, oldValue, newValue);
        }
    }
}

public class Property<T> implements SourcesPropertyChangeEvents, PropertyChangeListener, AsyncCallback<T> {
    private final String name;
    private T value;
    private PropertyChangeListenerCollection listeners;

    public Property(final String name) {
        this(name, null);
    }

    public Property(final String name, final T initialValue) {
        this.name = name;
        this.value = initialValue;
    }

    public void set(final T newValue) {
        final T oldValue = value;
        value = newValue;

        if(listeners != null) {
            listeners.firePropertyChangeEvent(this, name, oldValue, newValue);
        }
    }

    public T get() {
        return value;
    }

    public void onSuccess(final T newValue) {
        set(newValue);
    }

    public void onFailure(final Throwable error) {
        if(GWT.getUncaughtExceptionHandler() != null) {
            GWT.getUncaughtExceptionHandler().onUncaughtException(error);
        }
    }

    public void propertyChanged(final SourcesPropertyChangeEvents source, final String propertyName,
        final Object oldValue, final Object newValue) {

        set(newValue);
    }

    public void addPropertyChangeListener(final PropertyChangeListener listener) {
        if(listeners != null) {
            listeners = new PropertyChangeListenerCollection();
        }

        listeners.add(listener);
    }

    public void addPropertyChangeListener(final String propertyName, final PropertyChangeListener listener) {
        if(listeners != null) {
            listeners = new PropertyChangeListenerCollection();
        }

        listeners.add(propertyName, listener);
    }

    public void removePropertyChangeListener(final PropertyChangeListener listener) {
        if(listeners != null) {
            listeners.remove(listener);
        }
    }

    public void removePropertyChangeListener(final String propertyName, final PropertyChangeListener listener) {
        if(listeners != null) {
            listeners.remove(propertyName, listener);
        }
    }

}

Instead of storing your bindable properties as normal fields: you simply wrap them in Property objects. You can then use the fact that Property objects both produce and consume propertyChangeEvents to bind them together, and even pass them into RPC methods to be set when the server hands the data back to you.

They rely on a PropertyChangeListenerCollection class that I haven’t given here, but it’s a simple enough class to write.

11-November-2008: I added in an implementation of the PropertyChangeListenerCollection class.

Give it a try, it makes life a surprising amount easier considering it’s size.

Advertisements

5 Responses to “Easy Property Binding and Aync Callbacks in GWT”

  1. Leonardo Says:

    Nice! Do you have an code sample?

  2. Using PropertyChangeListeners to ease GWT Async Calls « Techno Lemming Says:

    […] Java, Programming, Software, Technical, Web 2.0 — Jason @ 12:32 pm A while back I blogged a simple set of classes to make Property Binding easier in GWT, it’s also tied to GWT’s RPC system. I did not, […]

  3. EoD SQL Applied – Part 5 / 5 (GWT Applications) « Lemming Technology Blog Says:

    […] chore, since you need to work with a lot of callbacks (a nice way of abstracting lazy-like-loading is posted on my blog). This means that most of the object model given to GWT needs to be (at least partly) […]

  4. Bennett Edmerson Says:

    Im glad I found this site, I couldnt obtain any info on this matter before. I also run a site and if you are ever interested in doing some guest writing for me make sure you feel free to let me know, im always look for people to check out my blog page. Please stop by and leave a comment sometime!

  5. Sachin Says:

    The trade-off would be in the use of causing RPC whitelisting of GWT Compile/Runtime becoming slower. This would be make it nearly unusable in big projects ( 1000’s of domain models )


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

%d bloggers like this: