BioJava:Tutorial:Implementing Changeable

Tutorial

By Matthew Pocock

We are going to implement a simple ChangeEvent source that stores a string name property and can inform other objects if this name changes. By the end of this tutorial you should be comefortable with the general issues surrounding implementing event sources and for ensuring that resources are allocated as needed.

The Nameable interface

By convention, BioJava always defines changes in an interface. This allows a range of implementations to provide a unified API to a change without mandaiting them to shair any code. We will define the Nameable interface.

package demos.Changeable;

import org.biojava.utils.*;

public interface Nameable extends Changeable {

The first thing we must do is define the ChangeType that indicates that the name has changed. By convention, it is a public static final field of the interface and is named in upper-case, with word boundaries indicated by underscores. The constructor needs a description, and also the name of the current class and the name of the field. This is so that during serialization, the ChangeType instance will resolve correctly both over time and between VMs.

  /**
   * The ChangeType that indicates that the name property has changed.
   */
  public static final ChangeType NAME = new ChangeType(
    "The name has changed.", // human-readable description
    "demos.Changeable",      // the current class name
    "NAME"                   // field name
  );

Now we have the definition of the accessor methods.

  /**
   * Return the name associated with this Nameable.
   *
   * @return the name property
   */
  public String getName();

  /**
   * Change the name associated with this Nameable.
   *
   * @param the new value for the name property
   * @throws ChangeVetoException if for any reason the name could not be set
   */
  public void setName(String name)
  throws ChangeVetoException;
}

and that’s it for the Nameable interface.

The simplest implementation - extend AbstractChangeable

The simplest way to implement the Nameable interface is to inherit from AbstractChangeable. This is the aproach we will take here. Firstly we will define the class and add a couple of constructors.

package demos.Changeable;

import org.biojava.utils.*;

public class SimpleChangeable
extends AbstractChangeable
implements Nameable {
  private String name;

  public SimpleChangeable() {
    this(null);
  }

  public SimpleChangeable(String name) {
    this.name = name;
  }

The getName method can also be written in the obvious way.

  public String getName() {
    return name;
  }

The process of informing listeners requires some bagage to be present - in particular, the list of listeners. This would impose overhead on all instances of Changeable, regardless of whether listeners exist or not. The solution to this is to lazily instantiate the supporting objects. Fortunately, AbstractChangeable handles all of this for you. The two methods you need to use are hasListeners(), which will return true if there are any listeners at all and false otherwise. If there are no listeners, then the name can be set directly.

  public void setName(String name)
  throws ChangeVetoException {
    if(!hasListeners()) {
      this.name = name;
    } else {

If there are listeners, then the method getChangeSupport is used to retrieve the ChangeSupport instance that maintains the listeners list. You should then synchronize on this to ensure that no listeners are added or removed while the name is being set.

      ChangeSupport cs = getChangeSupport(Nameable.NAME);
      synchronized(cs) {

Next, we make a new ChangeEvent to describe how the object wishes to alter, we fire a preChange notification to the listeners so that they have a chance to veto the change, we make the change and lastly we inform the listeners that the change has been made.

        ChangeEvent ce = new ChangeEvent(this, Nameable.NAME, name, this.name);
        cs.firePreChange(ce);
        this.name = name;
        cs.firePostChange(ce);
      }
    }
  }
}

That is the end of the implementation.

Using ChangeSupport directly

The previous example used ChangeSupport to store a list of listeners but via the AbstractChangeable class. Java only allows classes to inherit from one other class. This means that if you have a class that must implement Changeability but already is derived from another class, you can’t use AbstractChangeable. You can, however, still use ChangeSupport. To illustrate this, we will look at the code in AbstractChangeable that wires in the ChangeSupport object.

AbstractChangeable is in the package org.biojava.utils, and implements Changeable. It is abstract as you must sub-class to provide code to actualy fire events.

package org.biojava.utils;

public abstract class AbstractChangeable implements Changeable {

The listener networks are not preserved during serialization. This is partly to prevent arbitrarily large networks of objects being dumped, and partly because listeners can be safely added in custom serialization/deserialization code.

  private transient ChangeSupport changeSupport = null;

The hasListeners method is implemented in the obvious way. It is protected, because it is realy a memory optimization method, and not part of the external interface of extending classes.

  protected boolean hasListeners() {
    return changeSupport != null;
  }

To retrieve the ChangeSupport delegate, we need to provide an access method. Again, this is protected and implemented in the obvious way.

  protected ChangeSupport getChangeSupport(ChangeType ct) {
    if(changeSupport == null) {
      changeSupport = new ChangeSupport();
    }

    return changeSupport;
  }

Some subclasses may wish to override this method and lazily instantiate resoruces when the first listener for a particular ChangeType is added. In this case, the overriden method should first call super.getChangeSupport and then perform any checkes it wishes.

Now that the protected methods are in place, we can provide the bodies of the listener management methods. These firstly use getChangeSupport to retrieve the delegate, and then ask it to add or remove a listener. We must synchronize on the delegate to make sure that it maintains in a consistent state.

  public void addChangeListener(ChangeListener cl) {
    ChangeSupport cs = getChangeSupport(null);
    synchronized(cs) {
      cs.addChangeListener(cl);
    }
  }

  public void addChangeListener(ChangeListener cl, ChangeType ct) {
    ChangeSupport cs = getChangeSupport(ct);
    synchronized(cs) {
      cs.addChangeListener(cl, ct);
    }
  }

  public void removeChangeListener(ChangeListener cl) {
    ChangeSupport cs = getChangeSupport(null);
    synchronized(cs) {
      cs.removeChangeListener(cl);
    }
  }

  public void removeChangeListener(ChangeListener cl, ChangeType ct) {
    ChangeSupport cs = getChangeSupport(ct);
    synchronized(cs) {
      cs.removeChangeListener(cl, ct);
    }
  }
}

And that is the end of the class. You should be able to cut-and-paste this code into your own Changeable objects to implement the basic delegate-management.

Using an abstract class to provide the event handeling

Often there are a number of implementatoins of an interface that are almost exactly the same except for the particulars of how data is stored. It is a shame to write the event code multiple times. A useful design pattern for this is to provide an abstract class that takes care of all the synchronization issues and calles stub methods to perform the actual access to object state. Here is an example of that for the Nameable class.

The abstract class will look like this.

public abstract class AbstractNameable implements Nameable {
  public void setName(String name)
  throws ChangeVetoException {
    if(!hasListeners()) {
      setNameImpl(name);
    } else {
      ChangeSupport cs = getChangeSupport(Nameable.NAME);
      synchronized(cs) {
        ChangeEvent ce = new ChangeEvent(this, Nameable.NAME, name, this.name);
        cs.firePreChange(ce);
        setNameImpl(name);
        cs.firePostChange(ce);
      }
    }
  }

  protected abstract void setNameImpl(String name)
  throws ChangeVetoException;
}

The implementation would look something like this.

public class MyNameable extends AbstractNameable {
  private String name;

  public String getName() {
    return this.name;
  }

  public void setName(String name)
  throws ChangeVetoException {
    this.name = name;
  }
}

This split between the abstract implementation that handles all of the event guts and a realy light-weight implementation that controls access to data-storage is very useful in practice, and is used extensively in BioJava, particularly in the org.biojava.bio.dist package.

What next?

By now, you should be able to define interfaces that are Changeable, and to write implementations of these interfaces using AbstractChangeable or by delegating to ChangeSupport directly. For cases where there are many implementations that differ only in the means of data-storage, you should be able to factor the Changeablility code into an abstract class, and subclass this for each form of data-access.