Atom Feed SITE FEED   ADD TO GOOGLE READER

Seeking feedback: ListEvent, ListEventBuilder APIs

As of today, beansbinding does a very lightweight approach to observable lists. There's two interfaces, ObservableList.java and ObservableListListener.java.

I'm refining a proposal for a more heavyweight approach. In addition to simplified List and Listener interfaces, I add a ListEvent interface and a ListEventBuilder class.

Motivation for these interfaces:
  • Support for fine-grained events. Now List.removeAll can fire a single event.
  • Support for move events. This allows listeners to track state as elements are reordered or sorted by the user.
  • Support for event 'envelopes'. This allows the user of an ObservableList to prevent observers from seeing intermediate states.

    Fine-grained events are a central part of the well-loved Glazed Lists project, and I think they could be very helpful in the JDK.

    If you could read through this code and email me your comments, I'd appreciate it:
    ListEvent
    ListEventBuilder
  • Use Shift+Enter for multiline text in Gmail Chat

    My IM client at work is the Google Talk widget that's built-in to Gmail. It works okay, but it's annoying for typing code snippets into chats. Code snippets require multiple lines of text, but pressing Enter commits the message. How to send a multi-line message?

    Well today I accidentally discovered that pressing Shift+Enter inserts a newline character in to the chat. This might work in other IM clients as well...

    Properties should be stateless to avoid memory leaks

    In the latest rev of beansbinding, the AbstractProperty class is stateful. This means that when you add a listener to an object, the property independently remembers both.

    For example, suppose your code uses databinding in a dialog:
      private static final Property textFieldTextProperty
    = new BeanProperty<JTextField,String>("text");
    private static final Property customerNameProperty
    = new BeanProperty<Customer,String>("name");

    public JDialog createDialog() {
    JTextField textField = new JTextField();
    Customer customer = new Customer();
    Binding customerNameBinding = new Binding<JTextField,String,Customer,String>(
    textField, textFieldTextProperty, customer, customerNameProperty);
    customerNameBinding.bind();
    ...
    }
    After the dialog has been dismissed, the form and its data should fall out-of-scope and get garbage collected. But they don't! Since the textFieldTextProperty field is static and holds a reference to the dialog, the dialog remains in memory until the application exits. This invisible memory leak exists because the addPropertyStateListener method has a hidden side effect.

    In this case, the solution is to save a reference to the customerNameBinding and to unbind() it upon dialog closing. Alternately, recreating new Property objects every time the dialog is created will avoid the problem.

    Perhaps instead, beansbinding will make Properties stateless.

    Chaining Stateless Properties

    In a recent post, I described how I implemented the observers for stateless properties. In this follow up I describe how to do observers for chained properties.

    What's a chained property?
    Suppose I'd like to bind a combo box to a customer's country. If the Customer bean has a getCountry() property, that's easy. But if the Country is only exposed via a series of getters like getBillingAddress().getCountry(), I bind to Customer#address and address#country in series. A chained property is created by composing multiple properties into one.

    Implementing get() and set() is easy
    I resolve each property in the chain using the value of one property as the bean for the next:
    public class ChainedProperty<B,V> implements Property<B,V> {
    private final List<Property> propertyChain;
    public ChainedProperty(...) { ... }

    public V get(B bean) {
    Object current = bean;
    for (Property property : propertyChain) {
    current = property.get(current);
    }
    return (V)current;
    }

    public void set(B bean, V value) {
    Object current = bean;
    for (Iterator<Property> m = propertyChain.iterator(); m.hasNext(); ) {
    Property property = m.next();

    if (m.hasNext()) {
    current = property.get(current);
    } else {
    property.set(current, value);
    }
    }
    }
    ...
    }
    Implementation Plan for Chaining Observers
    1. Observe the bean for each property in the chain.
    2. When a property changes, action is required:
      • If it's the last property whose value has changed, notify the chain's listener
      • For other properties, change the observed bean for the next property in the chain

    Each time a new listener is registered on the chain, a new chain of SubListeners is created. Each SubListener observes a single property of a single bean. And so the chain of SubListeners observes the chain of properties. We use a custom SubListener at the end of the chain who notifies the PropertyChangeListener:
      public abstract class SubListener<B, V> implements PropertyChangeListener {
    private final Property<B, V> property;
    private B bean;
    private V currentValue;
    public SubListener(...) { ... }

    void setBean(B bean) {
    property.removePropertyChangeListener(bean, this);
    this.bean = bean;
    property.addPropertyChangeListener(bean, this);
    valueChanged();
    }

    public void propertyChange(PropertyChangeEvent evt) {
    valueChanged();
    }

    private void valueChanged() {
    V oldValue = currentValue;
    currentValue = property.get(bean);
    fireValueChanged(oldValue, currentValue);
    }

    abstract void fireValueChanged(V oldValue, V currentValue);
    }

    public class ChainingSubListener<B, V> extends SubListener<B, V> {
    private final SubListener<V, ?> next;
    public ChainingSubListener(...) { ... }

    @Override
    public void fireValueChanged(V oldValue, V currentValue) {
    next.setBean(currentValue);
    }
    }

    public class LastSubListener<B, V> extends SubListener<B, V> {
    private final Object rootBean;
    private final String chainName;
    private final PropertyChangeListener delegate;
    public LastSubListener(...) { ... }

    @Override
    public void fireValueChanged(V oldValue, V currentValue) {
    delegate.propertyChange(
    new PropertyChangeEvent(rootBean, chainName, oldValue, currentValue));

    }
    }
    Removing Observers
    Just as with my previous solution, I can support removing the chain of listeners with a carefully crafted equals() method. Two SubListeners are equal if they model the same property, have the same chain of SubListeners downstream, and notify the same PropertyChangeListener at the chain's end.

    ChainedProperty.java source listing

    Implementing Observers with Stateless Properties

    I've got a simplified Property interface and a mixin that makes the property observable:
    public interface Property<B,V> {
    V get(B bean);
    void set(B bean, V value);
    }

    public interface ObservableProperty<B,V> extends Property <B,V> {
    void addPropertyChangeListener(B bean, PropertyChangeListener p);
    void removePropertyChangeListener(B bean, PropertyChangeListener p);
    }
    For most use cases, the interface is implemented by a library. For example, this instance uses the getters and setters:
      ObservableProperty p = new BeanProperty(Customer.class, "address");
    Today I'm interested in the implementation of this interface. Here's a very straightforward implementation that doesn't even need reflection - getting the Document property from a JTextField:
    public class JTextFieldDocumentProperty 
    implements ObservableProperty<JTextField, Document> {

    public Document get(JTextField bean) {
    return bean.getDocument();
    }

    public void set(JTextField bean, Document value) {
    bean.setDocument(value);
    }

    public void addPropertyChangeListener(JTextField bean, PropertyChangeListener p) {
    bean.addPropertyChangeListener("document", p);
    }

    public void removePropertyChangeListener(JTextField bean, PropertyChangeListener p) {
    bean.removePropertyChangeListener("document", p);
    }
    }
    This code is deceivingly simple because the listener types in the Property interface (PropertyChangeListener) exactly match the listener types in the bean. Things get a much more interesting if the listener types are different.

    Using an adapter
    The natural solution is to code the addPropertyChangeListener to use a simple adapter to convert events of one type to events of another type. This adapter converts between DocumentListener and PropertyChangeListener:
    private class DocumentToPropertyChangeListener
    implements DocumentListener {
    private String currentText;
    private final Document document;
    private final PropertyChangeListener delegate;

    public DocumentToPropertyChangeListener(Document bean, PropertyChangeListener p) {
    this.document = bean;
    this.delegate = p;
    this.currentText = getDocumentText();
    }

    private String getDocumentText() {
    try {
    return document.getText(0, document.getLength());
    } catch (BadLocationException e) {
    throw new RuntimeException(e);
    }
    }

    public void insertUpdate(DocumentEvent e) {
    fireChanged();
    }
    public void removeUpdate(DocumentEvent e) {
    fireChanged();
    }
    public void changedUpdate(DocumentEvent e) {
    fireChanged();
    }
    public void fireChanged() {
    String oldText = currentText;
    currentText = getDocumentText();
    delegate.propertyChange(
    new PropertyChangeEvent(document, "text", oldText, currentText));
    }
    }
    As you can see, the Document class is a bit cumbersome to use. But this interface correctly adapts the listener types. Unfortunately, we still have the trickiest problem ahead of us...

    The problem with removePropertyChangeListener()
    In my second example, I'll implement Property for the text of a Document. The implementation of addPropertyChangeListener is easy; we just create the adapter inline:
    public class DocumentTextProperty 
    implements StatelessProperty<Document, String> {
    ...
    public void addPropertyChangeListener(Document bean, PropertyChangeListener p) {
    bean.addDocumentListener(new DocumentToPropertyChangeListener(bean, p));
    }
    public void removePropertyChangeListener(Document bean, PropertyChangeListener p) {
    ...
    }
    }
    But how does remove work? There's a big problem - we didn't save a reference to our adapter! We need to recover that reference somehow so we can remove the same adapter that was added.

    The Document interface doesn't expose its listeners; ie. there's no getDocumentListeners() method. So we can't just iterate over the document's listeners to find the matching adapter.

    If we store a reference to the adapter in the DocumentTextProperty object, then we'll almost certainly have a memory leak. Even if we stored just a WeakReference<DocumentTextProperty>, it would make our Property stateful. In the ideal implementation, any two instances of the same property should be interchangeable. For example, we shouldn't force the user to store a reference to the Property instance. This code should work fine but it won't work if the property is stateful:
      public void installListeners() {
    new DocumentTextProperty().addPropertyChangeListener(
    myDocument, myPropertyChangeListener);
    }
    ...
    public void shutdown() {
    new DocumentTextProperty().removePropertyChangeListener(
    myDocument, myPropertyChangeListener);
    }

    The next option is to store this state in a static global registry. This is the approach taken by the bean-properties Java.net project. I don't like this approach because it feels very heavyweight:
  • It needs WeakReferences.
  • The shared registry requires synchronization.
  • There's memory overhead to store each registered bean/listener pair.
  • The registry is complex because it requires each listener pair to be uniquely identified.
  • It prevents bean/listener pairs from being serialized.

    Fortunately, there is a clever solution to this problem - don't store a reference to the adapter. Instead, when we call removePropertyChangeListener, we pass in a different instance that equals the first:
    public class DocumentTextProperty 
    implements StatelessProperty<Document, String> {
    ...
    public void addPropertyChangeListener(Document bean, PropertyChangeListener p) {
    bean.addDocumentListener(new DocumentToPropertyChangeListener(bean, p));
    }
    public void removePropertyChangeListener(Document bean, PropertyChangeListener p) {
    bean.removeDocumentListener(new DocumentToPropertyChangeListener(bean, p));
    }
    }
    This requires us to implement equals and hashCode in our adapter:
    private class DocumentToPropertyChangeListener 
    implements DocumentListener {
    public boolean equals(Object obj) {
    return obj != null
    && obj.getClass() == this.getClass()
    && ((DocumentToPropertyChangeListener) obj).delegate.equals(delegate)
    && ((DocumentToPropertyChangeListener) obj).document.equals(document);
    }
    public int hashCode() {
    return delegate.hashCode() * 37 + document.hashCode();
    }
    }

    I love equals()
    We've taken advantage of one of Java's greatest features: a strong concept of equality. It makes our solution stateless, immutable, serializable, efficient and easy to use.
  • Stop Writing Redundant Javadoc

    I keep seeing this exact same Javadoc being written over and over again:
    /**
    * A pizza delivery.
    *
    * @author jessewilson
    */
    public class PizzaDelivery {

    ...

    /**
    * Sets the address of this PizzaDelivery.
    *
    * @param address the address to set.
    */
    public void setAddress(Address address) {
    ...
    }
    }
    Naturally, this doc took the code author time to write. He typed 'address' an extra 3 times. But I'm not concerned with the author's time; he's got lots of free time on his hands. My problem is that he's wasting his team's time and hurting the code.

    Redundant Javadoc decreases the signal-to-noise ratio of the code. It trains your team that documentation is just a hoop that needs to be jumped through. It shows that Javadoc is for writing, not for reading. And that you think it's okay to put zero thought into documentation.

    If you have nothing nice* to say, say nothing. If your code style guide makes Javadoc mandatory, change the style guide. A well-chosen method or class name is often enough.

    Returning boolean isn't always that helpful

    In complex systems, transparency can sometimes help. In my fake pizzastore application, I've got an API that's used to control whether the Place Order! button is enabled or disabled:
      public boolean isOrderValid(PizzaOrder pizzaOrder) {
    if (!geography.isAcceptableAddress(pizzaOrder.getAddress())) {
    return false;
    }

    if (!deliveryRules.isLargeEnoughOrder(pizzaOrder)) {
    return false;
    }

    List<Store> storesInRange = storesRegistry.getStoresWithinRange(
    pizzaOrder.getAddress(), MAXIMUM_DELIVERY_RADIUS);
    if (storesInRange.isEmpty()) {
    return false;
    }

    return true;
    }
    It works well. But sometimes I'll get a bug report complaining that the button is disabled when it shouldn't be. The problem isn't that the button is disabled, it's that the user doesn't know why the button is disabled. The fix is easy:
      public List<String> getReasonsWhyOrderIsInvalid(PizzaOrder pizzaOrder) {
    List<String> reasons = new ArrayList<String>();

    if (!geography.isAcceptableAddress(pizzaOrder.getAddress())) {
    reasons.add("Unacceptable address");
    }

    if (!deliveryRules.isLargeEnoughOrder(pizzaOrder)) {
    reasons.add("Order is not large enough");
    }

    List<Store> storesInRange = storesRegistry.getStoresWithinRange(
    pizzaOrder.getAddress(), MAXIMUM_DELIVERY_RADIUS);
    if (storesInRange.isEmpty()) {
    reasons.add("No stores in range of " + pizzaOrder.getAddress());
    }

    return reasons;
    }

    public boolean isOrderValid(PizzaOrder pizzaOrder) {
    return getReasonsWhyOrderIsInvalid(pizzaOrder).isEmpty();
    }
    I can use the list of warnings as a tooltip on the disabled button, or use more sophisticated reporting as necessary. This works really nicely, and it keeps the end users informed.

    Applying this technique elsewhere
    There's lots of complex rulesets throughout the code. On one screen we show a tab with navigation data for deliveries in progress. That tab is displayed only if the navigation feature is configured, the user has sufficient credentials, and if GPS reports are being received.

    By displaying the reasons why the navigation tab is not shown on an admin page, I can diagnose problems without a debugger. Reasons are more helpful than booleans because they guide me in my investigation. This approach works nicely in webapps, where it's easy to include extra admin pages.

    In my app, reasons pages provide every detail on why the application is behaving the way it is - transparency is great!