Change notifications
From JMoney
Plug-ins may need to know when changes have been made to the datastore. The plug-ins can then take action such as updating other tables in the datastore or displaying a message to the user.
Adding the Listener
Plug-ins can listen for changes to the datastore (new objects being added, objects being deleted, or property value changes) by adding a listener to a DataManager object. DataManager is an abstract base class for all classes that represent a datastore. Classes are derived from DataManager for each implementation of a particular datastore.
The TransactionManager class also extends DataManager, so you can add a listener to a transaction manager to recieve events for changes to the uncommitted data. Normally a plug-in would listen to changes to the committed datastore. However you may have a view in which user can make many changes before pressing 'OK' to commit the changes or 'cancel'. If the view contained, say, a table of data then that table would want to listen to changes in the uncommitted data.
JMoney supports a single open session. You can get a reference to the DataManager for this session by calling JMoneyPlugin.getSessionManager(). A DataManager object exists only while a session is open, being constructed when, for example, File, Open... is selected, and being released for garbage collection when File, Close is selected or another session is opened.
There are two methods for adding a listener to these objects:
- DataManager.addChangeListenerWeakly(listener). This adds a listener using weak references. The DataManager object keeps only a weak reference to the listener, so when there are no other reachable references to the listener, the listener is removed. This avoids the need for the listening code to remove the listener.
- addChangeListener(listener, control). This method will add the listener and also listen for when the given control is destroyed and remove the listener at that time. Typically you would pass in the parent control that contains the controls that contain the data that the listener is updating.
Be carful when adding a listener weakly. You must keep a reference to the listener yourself. Do not add a listener using:
dataManager.addChangeListenerWeakly(new MyListener());
As there are no references to the listener other than the weak reference in the listener list, the listener could be removed at any time.
Adding the Listener to the JMoney Activator
You may add a listener to the JMoneyPlugin object using addSessionChangeListener method in that object. This is a helper method that should be used by any object that remains active after the current session is closed. The listener will be notified of all changes to the current session. If the current session is replaced then the listener is told. If a new session replaces the old session then the listener will then recieve changes to the new session.
The listener interface used by the JMoneyPlugin object extends the interface used by the DataManager object, adding the sessionReplaced method. The sessionReplaced method is called when a new session is opened or the session is closed.
The JMoneyPlugin object only supports the 'lifetime of control' method for adding a listener. It has no support for adding a listener weakly.
Currently the only JMoney view is the navigation view. This view stays open when a session is closed, unlike the editors which all close when a session is closed. If you want to add a view then you will need to decide whether the view should close when the session closes (in which case how do you get the view back when another session is opened) or whether the view stays open (in which case you should display some message to indicate there is no session open).
Implementing the Listener
The listener has methods for recieving changes to the data (new objects, deleted objects, and changes to property values).
You may wonder why there is not a set of three such methods for each object type, e.g. entryAdded, accountAdded, transactionAdded etc. The reason is that the set of object types is not known because the data model is extensible and plug-ins may add further classes of objects.
Therefore typically you will need to check the object class. For example, if you want to take action when an entry is reconciled, you will need the following:
void objectCreated(ExtendableObject newObject) {
if (newObject instanceof Entry) {
Entry entry = (Entry)newObject;
int status = entry.getPropertyValue(ReconciliationEntryInfo.getStatusAccessor());
if (status == ReconciliationEntry.CLEARED) {
processNewlyReconciledEntry(entry);
}
}
}
void objectChanged(ExtendableObject changedObject, PropertyAccessor_Scalar changedProperty, Object oldValue, Object newValue) {
if (changedObject instanceof Entry
&& changedProperty == ReconciliationEntryInfo.getStatusAccessor()
&& ((Integer)oldValue).intValue != ReconciliationEntry.CLEARED
&& ((Integer)newValue).intValue == ReconciliationEntry.CLEARED) {
processNewlyReconciledEntry((Entry)changedObject);
}
}
Note that the listener inteface contains methods objectInserted and objectCreated. The latter is called for all newly created objects and is the method you should use in this example. The objectInserted method is not called for objects that are added along with their parent object. For example, if a Transaction object is added then objectInserted is called for the Transaction object only and not for its Entry objects, whereas objectCreated is called for both the Transaction object and all its Entry objects.
