Programming__WebObjects-EOF-Using EOF-Delegates and Notifications

Version 4.1 by smmccraw on 2007/07/08 09:44

EO Change Notifications

Pierre Bernard

To invalidate cached versions of derived values, you might want to use this:

  / Utility class. Provides for a way to watch for changes in EOs. The main use of this would be to

  • be able to safely keep cached/computed/derived values that get cleared once out of sync.

  • @author bernard
  • @version ChangeNotificationCenter.java,v 1.1 2003/03/11 16:26:59 bernard Exp
     /
      public class ChangeNotificationCenter
      {
        Public class constants
     
        /
     Name of the posted notification
         */
        public static final String OBJECT_CHANGED = "MBSObjectChanged";
     
        / Possible type of change a notification is posted for
         */
        public static final String INVALIDATION = "Invalidation";
     
        /
    Possible type of change a notification is posted for
         */
        public static final String DELETION = "Deletion";
     
        / Possible type of change a notification is posted for
         */
        public static final String UPDATE = "Update";
     
        
     Private class constants
     
        /
    Key in the posted notification's userInfo dictionary.
         */
        private static final String OBJECT = "MBSObject";
     
        / Key in the posted notification's userInfo dictionary.
         */
        private static final String TYPE = "MBSType";
     
        /
    Selector used for calling the watcher object upon a change
         */
        private static final NSSelector watchedObjectChangedSelector =
          new NSSelector("watchedObjectChanged", new Class[] { NSNotification.class });
     
        Private class variables
     
        / Reference holding the shared singleton instance.
         */
        private static ChangeNotificationCenter defaultCenter = null;
     
        
     Constructor
     
        /
    Singleton class
         */
        private ChangeNotificationCenter()
        {
          super();
     
          Class[) notificationArray = new Class(] { NSNotification.class };
          NSSelector objectsChangedIInEditingContextSelector =
            new NSSelector("objectsChangedInEditingContext", notificationArray);
     
          NSNotificationCenter.defaultCenter().addObserver(
            this,
            objectsChangedIInEditingContextSelector,
            EOEditingContext.ObjectsChangedInEditingContextNotification,
            null);
        }
     
        Public insatnce methods
     
        / Method called when a change notification is received.

  • The notification is split and redispatched for all updated objects.
         */
        public void objectsChangedInEditingContext(NSNotification notification)
        {
          NSArray updated = (NSArray) notification.userInfo().objectForKey(EOObjectStore.UpdatedKey);
     
          if (updated !=_null)
    {
    int_count_=_updated.count();

    for_(int_i_=_0;_i_<_count;_i++)
    {
    Object_object_=_updated.objectAtIndexinformation;
    NSDictionary_userInfo_=
    new_NSDictionary(new_Object[)_(_object,_UPDATE_),_new_Object(]_{_OBJECT,_TYPE_});
    NSNotificationCenter.defaultCenter().postNotification(OBJECT_CHANGED,_object,_userInfo);
    }
    }

    NSArray_invalidated_=_(NSArray)_notification.userInfo().objectForKey(EOObjectStore.InvalidatedKey);

    if_(invalidated_!= null)
          {
            int count = invalidated.count();
     
            for (int i = 0; i < count; i++)
            {
              Object object = invalidated.objectAtIndexinformation;
              NSDictionary userInfo =
                new NSDictionary(new Object[) ( object, INVALIDATION ), new Object(] { OBJECT, TYPE });
              NSNotificationCenter.defaultCenter().postNotification(OBJECT_CHANGED, object, userInfo);
            }
          }
     
          NSArray deleted = (NSArray) notification.userInfo().objectForKey(EOObjectStore.DeletedKey);
     
          if (deleted != null)
          {
            int count = deleted.count();
     
            for (int i = 0; i < count; i++)
            {
              Object object = deleted.objectAtIndexinformation;
              NSDictionary userInfo = new NSDictionary(new Object[) ( object, DELETION ), new Object(] { OBJECT, TYPE });
              NSNotificationCenter.defaultCenter().postNotification(OBJECT_CHANGED, object, userInfo);
            }
          }
        }
     
        / Method to be called when one creates a cached value that depends on the attributes of a given object.<BR>

  • Upon registration the object is watched and the watchedObjectChanged() is called once it changes, thus providing an
  • oppurtunity to invalidate the cached value.<BR>

  • @param watcher the watching object which should unregister before disposing
  • @param object the object to watch
  • @see #unregisterCacheDependancy
  • @see #unregisterCacheDependancies
         */
        public void registerCacheDependancy(ObserverInterface watcher, EOEnterpriseObject object)
        {
          NSNotificationCenter.defaultCenter().addObserver(watcher, watchedObjectChangedSelector, OBJECT_CHANGED, object);
        }
     
        / Method to be called when one abandons or clears a cached value that depended on attributes of a given object.<BR>

  • @param watcher the watching object 
  • @param object the object to watch
  • @see #registerCacheDependancy
         */
        public void unregisterCacheDependancy(ObserverInterface watcher, Object object)
        {
          NSNotificationCenter.defaultCenter().removeObserver(watcher, OBJECT_CHANGED, object);
        }
     
        / Unregisters all of the callers dependancies. To be used sparingly as it may break unknown but required
  • dependancies. Usually called when disposing the calling object.<BR>

  • @param watcher the watching object 
  • @see #registerCacheDependancy
  • @see #unregisterCacheDependancy
         */
        public void unregisterCacheDependancies(ObserverInterface watcher)
        {
          NSNotificationCenter.defaultCenter().removeObserver(watcher, OBJECT_CHANGED, null);
        }
     
        Public class methods
     
        / Gets or lazily instantiates the shared instance of the ChangeNotificationCenter

  • @return the unique instance
         */
        public static ChangeNotificationCenter defaultCenter()
        {
          if (defaultCenter h1.  null)
          {
            createDefaultCenter();
          }
     
          return defaultCenter;
        }
     
        / Utility method. Allows for retrieving the changed object from a notification
  • that results of registering interest in a change.

  • @param notification the received notification
  • @return the object that triggered the notification
         */
        public static EOEnterpriseObject objectFromNotification(NSNotification notification)
        {
          return (EOEnterpriseObject) notification.userInfo().objectForKey(OBJECT);
        }
     
        / Utility method. Allows for retrieving the type of change that ocurred from a notification
  • that results of registering interest in a change.

  • @param notification the received notification
  • @return the type as one of the class constants defined in ChangeNotificationCenter
         */
        public static String typeFromNotification(NSNotification notification)
        {
          return (String) notification.userInfo().objectForKey(TYPE);
        }
     
        Private class methods
     
        / Creates the singleton instance ensuring there is no existing instance.
         */
        private static synchronized void createDefaultCenter()
        {
          if (defaultCenter  null)
          {
            defaultCenter = new ChangeNotificationCenter();
          }
        }
     
        
     Inner interface definition
     
        /
    Interface to be implemented by objects that need to listen to changes.<BR>

  • CAVEAT: in most cases it is recommended to not directly implement the interface,
  • but rather create an inner class that implements the interface or better yet
  • extends the default implementation. Indeed if an object (e.g. an EO) which 
  • participates in an inheritance hierarchy is used as receiver for notifications,
  • registering or unregistering might break functionality in a parent class.
         */
        public static interface ObserverInterface
        {
          / Method called when an object on which locally cached values depend is modified.<BR>

  • This hook is provided as an opportunity to clear locally cached values. It's a good idea not to recreate
  • the cached values immediately, but on an as-needed basis. You should refrain from changing persisted EO
  • attributes from within this method as this might kick off another chain of notifications.<BR>

  • Once the caches cleared, it would be a very good idea to unregister from further notifications until
  • the cache is recreated.

  • @see lu.bcl.enterprise.entity.ChangeNotificationCenter#objectFromNotification
  • @see lu.bcl.enterprise.entity.ChangeNotificationCenter#registerCacheDependancy
  • @see lu.bcl.enterprise.entity.ChangeNotificationCenter#unRegisterCacheDependancy
           */
          public void watchedObjectChanged(NSNotification notification);
        }
     
        Inner class
     
        / Convenience class for implementing ObserverInterface.<BR>

  • The recommended way of registering for change notificications is to create an inner
  • class extending this one.
         */
        public abstract static class Observer implements ObserverInterface
        {
          / Method to be called by subclasses when the create a cached value that depends on the attributes of a given object.<BR>

  • Upon registration the object is watched and the clearCachedValues() is called once it changes, thus providing an
  • oppurtunity to invalidate the cached value.<BR>

  • You need to register for each object your locally cached values depend on. E.g. if you build a cached value from
  • attributes of EOs in a too-many relationship, your cache depends on each of the objects in the relationship as well
  • as on the source of the realtionship.<BR>

  • N.B. Extend the dispose() method to unregister any dependancy you might be registered for.

  • @param object the object to watch
  • @see #unregisterCacheDependancy
  • @see #clearCachedValues
           */
          protected void registerCacheDependancy(EOEnterpriseObject object)
          {
            ChangeNotificationCenter.defaultCenter().registerCacheDependancy(this, object);
          }
     
          / Method to be called by subclasses when they abandon or clear a cached value that depended on attributes
  • of a given object.<BR>
           *
  • @param object the object to watch
  • @see #registerCacheDependancy
  • @see #clearCachedValues
           */
          protected void unregisterCacheDependancy(EOEnterpriseObject object)
          {
            ChangeNotificationCenter.defaultCenter().unregisterCacheDependancy(this, object);
          }
     
          / Unregisters all of the callers dependancies. To be used sparingly as it may break unknown but required
  • dependancies. Usually called when disposing the calling object.<BR>

  • @see #registerCacheDependancy
  • @see #unregisterCacheDependancy
           */
          public void unregisterCacheDependancies()
          {
            ChangeNotificationCenter.defaultCenter().unregisterCacheDependancies(this);
          }
     
          / Overridden to unregister all cache dependancies
           */
          public void finalize() throws Throwable
          {
            ChangeNotificationCenter.defaultCenter().unregisterCacheDependancies(this);
     
            super.finalize();
          }
        }
      }

Category:WebObjects