Wiki source code of Programming__WebObjects-EOF-Using EOF-Delegates and Notifications
Version 4.1 by smmccraw on 2007/07/08 09:44
Show last authors
| author | version | line-number | content |
|---|---|---|---|
| 1 | == EO Change Notifications == | ||
| 2 | |||
| 3 | === Pierre Bernard === | ||
| 4 | |||
| 5 | To invalidate cached versions of derived values, you might want to use this: | ||
| 6 | |||
| 7 | {{panel}} | ||
| 8 | |||
| 9 | /** Utility class. Provides for a way to watch for changes in EOs. The main use of this would be to | ||
| 10 | * be able to safely keep cached/computed/derived values that get cleared once out of sync. | ||
| 11 | * | ||
| 12 | * @author bernard | ||
| 13 | * @version ChangeNotificationCenter.java,v 1.1 2003/03/11 16:26:59 bernard Exp | ||
| 14 | **/ | ||
| 15 | public class ChangeNotificationCenter | ||
| 16 | { | ||
| 17 | // Public class constants | ||
| 18 | |||
| 19 | /** Name of the posted notification | ||
| 20 | */ | ||
| 21 | public static final String OBJECT_CHANGED = "MBSObjectChanged"; | ||
| 22 | |||
| 23 | /** Possible type of change a notification is posted for | ||
| 24 | */ | ||
| 25 | public static final String INVALIDATION = "Invalidation"; | ||
| 26 | |||
| 27 | /** Possible type of change a notification is posted for | ||
| 28 | */ | ||
| 29 | public static final String DELETION = "Deletion"; | ||
| 30 | |||
| 31 | /** Possible type of change a notification is posted for | ||
| 32 | */ | ||
| 33 | public static final String UPDATE = "Update"; | ||
| 34 | |||
| 35 | // Private class constants | ||
| 36 | |||
| 37 | /** Key in the posted notification's userInfo dictionary. | ||
| 38 | */ | ||
| 39 | private static final String OBJECT = "MBSObject"; | ||
| 40 | |||
| 41 | /** Key in the posted notification's userInfo dictionary. | ||
| 42 | */ | ||
| 43 | private static final String TYPE = "MBSType"; | ||
| 44 | |||
| 45 | /** Selector used for calling the watcher object upon a change | ||
| 46 | */ | ||
| 47 | private static final NSSelector watchedObjectChangedSelector = | ||
| 48 | new NSSelector("watchedObjectChanged", new Class[] { NSNotification.class }); | ||
| 49 | |||
| 50 | // Private class variables | ||
| 51 | |||
| 52 | /** Reference holding the shared singleton instance. | ||
| 53 | */ | ||
| 54 | private static ChangeNotificationCenter defaultCenter = null; | ||
| 55 | |||
| 56 | // Constructor | ||
| 57 | |||
| 58 | /** Singleton class | ||
| 59 | */ | ||
| 60 | private ChangeNotificationCenter() | ||
| 61 | { | ||
| 62 | super(); | ||
| 63 | |||
| 64 | Class[) notificationArray = new Class(] { NSNotification.class }; | ||
| 65 | NSSelector objectsChangedIInEditingContextSelector = | ||
| 66 | new NSSelector("objectsChangedInEditingContext", notificationArray); | ||
| 67 | |||
| 68 | NSNotificationCenter.defaultCenter().addObserver( | ||
| 69 | this, | ||
| 70 | objectsChangedIInEditingContextSelector, | ||
| 71 | EOEditingContext.ObjectsChangedInEditingContextNotification, | ||
| 72 | null); | ||
| 73 | } | ||
| 74 | |||
| 75 | // Public insatnce methods | ||
| 76 | |||
| 77 | /** Method called when a change notification is received. | ||
| 78 | * | ||
| 79 | * The notification is split and redispatched for all updated objects. | ||
| 80 | */ | ||
| 81 | public void objectsChangedInEditingContext(NSNotification notification) | ||
| 82 | { | ||
| 83 | NSArray updated = (NSArray) notification.userInfo().objectForKey(EOObjectStore.UpdatedKey); | ||
| 84 | |||
| 85 | if (updated !=_null) | ||
| 86 | ______{ | ||
| 87 | ________int_count_=_updated.count(); | ||
| 88 | __ | ||
| 89 | ________for_(int_i_=_0;_i_<_count;_i++) | ||
| 90 | ________{ | ||
| 91 | __________Object_object_=_updated.objectAtIndex(i); | ||
| 92 | __________NSDictionary_userInfo_= | ||
| 93 | ____________new_NSDictionary(new_Object[)_(_object,_UPDATE_),_new_Object(]_{_OBJECT,_TYPE_}); | ||
| 94 | __________NSNotificationCenter.defaultCenter().postNotification(OBJECT_CHANGED,_object,_userInfo); | ||
| 95 | ________} | ||
| 96 | ______} | ||
| 97 | __ | ||
| 98 | ______NSArray_invalidated_=_(NSArray)_notification.userInfo().objectForKey(EOObjectStore.InvalidatedKey); | ||
| 99 | __ | ||
| 100 | ______if_(invalidated_!= null) | ||
| 101 | { | ||
| 102 | int count = invalidated.count(); | ||
| 103 | |||
| 104 | for (int i = 0; i < count; i++) | ||
| 105 | { | ||
| 106 | Object object = invalidated.objectAtIndex(i); | ||
| 107 | NSDictionary userInfo = | ||
| 108 | new NSDictionary(new Object[) ( object, INVALIDATION ), new Object(] { OBJECT, TYPE }); | ||
| 109 | NSNotificationCenter.defaultCenter().postNotification(OBJECT_CHANGED, object, userInfo); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | NSArray deleted = (NSArray) notification.userInfo().objectForKey(EOObjectStore.DeletedKey); | ||
| 114 | |||
| 115 | if (deleted != null) | ||
| 116 | { | ||
| 117 | int count = deleted.count(); | ||
| 118 | |||
| 119 | for (int i = 0; i < count; i++) | ||
| 120 | { | ||
| 121 | Object object = deleted.objectAtIndex(i); | ||
| 122 | NSDictionary userInfo = new NSDictionary(new Object[) ( object, DELETION ), new Object(] { OBJECT, TYPE }); | ||
| 123 | NSNotificationCenter.defaultCenter().postNotification(OBJECT_CHANGED, object, userInfo); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /** Method to be called when one creates a cached value that depends on the attributes of a given object.<BR> | ||
| 129 | * | ||
| 130 | * Upon registration the object is watched and the watchedObjectChanged() is called once it changes, thus providing an | ||
| 131 | * oppurtunity to invalidate the cached value.<BR> | ||
| 132 | * | ||
| 133 | * @param watcher the watching object which should unregister before disposing | ||
| 134 | * @param object the object to watch | ||
| 135 | * @see #unregisterCacheDependancy | ||
| 136 | * @see #unregisterCacheDependancies | ||
| 137 | */ | ||
| 138 | public void registerCacheDependancy(ObserverInterface watcher, EOEnterpriseObject object) | ||
| 139 | { | ||
| 140 | NSNotificationCenter.defaultCenter().addObserver(watcher, watchedObjectChangedSelector, OBJECT_CHANGED, object); | ||
| 141 | } | ||
| 142 | |||
| 143 | /** Method to be called when one abandons or clears a cached value that depended on attributes of a given object.<BR> | ||
| 144 | * | ||
| 145 | * @param watcher the watching object | ||
| 146 | * @param object the object to watch | ||
| 147 | * @see #registerCacheDependancy | ||
| 148 | */ | ||
| 149 | public void unregisterCacheDependancy(ObserverInterface watcher, Object object) | ||
| 150 | { | ||
| 151 | NSNotificationCenter.defaultCenter().removeObserver(watcher, OBJECT_CHANGED, object); | ||
| 152 | } | ||
| 153 | |||
| 154 | /** Unregisters all of the callers dependancies. To be used sparingly as it may break unknown but required | ||
| 155 | * dependancies. Usually called when disposing the calling object.<BR> | ||
| 156 | * | ||
| 157 | * @param watcher the watching object | ||
| 158 | * @see #registerCacheDependancy | ||
| 159 | * @see #unregisterCacheDependancy | ||
| 160 | */ | ||
| 161 | public void unregisterCacheDependancies(ObserverInterface watcher) | ||
| 162 | { | ||
| 163 | NSNotificationCenter.defaultCenter().removeObserver(watcher, OBJECT_CHANGED, null); | ||
| 164 | } | ||
| 165 | |||
| 166 | // Public class methods | ||
| 167 | |||
| 168 | /** Gets or lazily instantiates the shared instance of the ChangeNotificationCenter | ||
| 169 | * | ||
| 170 | * @return the unique instance | ||
| 171 | */ | ||
| 172 | public static ChangeNotificationCenter defaultCenter() | ||
| 173 | { | ||
| 174 | if (defaultCenter h1. null) | ||
| 175 | { | ||
| 176 | createDefaultCenter(); | ||
| 177 | } | ||
| 178 | |||
| 179 | return defaultCenter; | ||
| 180 | } | ||
| 181 | |||
| 182 | /** Utility method. Allows for retrieving the changed object from a notification | ||
| 183 | * that results of registering interest in a change. | ||
| 184 | * | ||
| 185 | * @param notification the received notification | ||
| 186 | * @return the object that triggered the notification | ||
| 187 | */ | ||
| 188 | public static EOEnterpriseObject objectFromNotification(NSNotification notification) | ||
| 189 | { | ||
| 190 | return (EOEnterpriseObject) notification.userInfo().objectForKey(OBJECT); | ||
| 191 | } | ||
| 192 | |||
| 193 | /** Utility method. Allows for retrieving the type of change that ocurred from a notification | ||
| 194 | * that results of registering interest in a change. | ||
| 195 | * | ||
| 196 | * @param notification the received notification | ||
| 197 | * @return the type as one of the class constants defined in ChangeNotificationCenter | ||
| 198 | */ | ||
| 199 | public static String typeFromNotification(NSNotification notification) | ||
| 200 | { | ||
| 201 | return (String) notification.userInfo().objectForKey(TYPE); | ||
| 202 | } | ||
| 203 | |||
| 204 | // Private class methods | ||
| 205 | |||
| 206 | /** Creates the singleton instance ensuring there is no existing instance. | ||
| 207 | */ | ||
| 208 | private static synchronized void createDefaultCenter() | ||
| 209 | { | ||
| 210 | if (defaultCenter null) | ||
| 211 | { | ||
| 212 | defaultCenter = new ChangeNotificationCenter(); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | // Inner interface definition | ||
| 217 | |||
| 218 | /** Interface to be implemented by objects that need to listen to changes.<BR> | ||
| 219 | * | ||
| 220 | * CAVEAT: in most cases it is recommended to not directly implement the interface, | ||
| 221 | * but rather create an inner class that implements the interface or better yet | ||
| 222 | * extends the default implementation. Indeed if an object (e.g. an EO) which | ||
| 223 | * participates in an inheritance hierarchy is used as receiver for notifications, | ||
| 224 | * registering or unregistering might break functionality in a parent class. | ||
| 225 | */ | ||
| 226 | public static interface ObserverInterface | ||
| 227 | { | ||
| 228 | /** Method called when an object on which locally cached values depend is modified.<BR> | ||
| 229 | * | ||
| 230 | * This hook is provided as an opportunity to clear locally cached values. It's a good idea not to recreate | ||
| 231 | * the cached values immediately, but on an as-needed basis. You should refrain from changing persisted EO | ||
| 232 | * attributes from within this method as this might kick off another chain of notifications.<BR> | ||
| 233 | * | ||
| 234 | * Once the caches cleared, it would be a very good idea to unregister from further notifications until | ||
| 235 | * the cache is recreated. | ||
| 236 | * | ||
| 237 | * @see lu.bcl.enterprise.entity.ChangeNotificationCenter#objectFromNotification | ||
| 238 | * @see lu.bcl.enterprise.entity.ChangeNotificationCenter#registerCacheDependancy | ||
| 239 | * @see lu.bcl.enterprise.entity.ChangeNotificationCenter#unRegisterCacheDependancy | ||
| 240 | */ | ||
| 241 | public void watchedObjectChanged(NSNotification notification); | ||
| 242 | } | ||
| 243 | |||
| 244 | // Inner class | ||
| 245 | |||
| 246 | /** Convenience class for implementing ObserverInterface.<BR> | ||
| 247 | * | ||
| 248 | * The recommended way of registering for change notificications is to create an inner | ||
| 249 | * class extending this one. | ||
| 250 | */ | ||
| 251 | public abstract static class Observer implements ObserverInterface | ||
| 252 | { | ||
| 253 | /** Method to be called by subclasses when the create a cached value that depends on the attributes of a given object.<BR> | ||
| 254 | * | ||
| 255 | * Upon registration the object is watched and the clearCachedValues() is called once it changes, thus providing an | ||
| 256 | * oppurtunity to invalidate the cached value.<BR> | ||
| 257 | * | ||
| 258 | * You need to register for each object your locally cached values depend on. E.g. if you build a cached value from | ||
| 259 | * attributes of EOs in a too-many relationship, your cache depends on each of the objects in the relationship as well | ||
| 260 | * as on the source of the realtionship.<BR> | ||
| 261 | * | ||
| 262 | * N.B. Extend the dispose() method to unregister any dependancy you might be registered for. | ||
| 263 | * | ||
| 264 | * @param object the object to watch | ||
| 265 | * @see #unregisterCacheDependancy | ||
| 266 | * @see #clearCachedValues | ||
| 267 | */ | ||
| 268 | protected void registerCacheDependancy(EOEnterpriseObject object) | ||
| 269 | { | ||
| 270 | ChangeNotificationCenter.defaultCenter().registerCacheDependancy(this, object); | ||
| 271 | } | ||
| 272 | |||
| 273 | /** Method to be called by subclasses when they abandon or clear a cached value that depended on attributes | ||
| 274 | * of a given object.<BR> | ||
| 275 | * | ||
| 276 | * @param object the object to watch | ||
| 277 | * @see #registerCacheDependancy | ||
| 278 | * @see #clearCachedValues | ||
| 279 | */ | ||
| 280 | protected void unregisterCacheDependancy(EOEnterpriseObject object) | ||
| 281 | { | ||
| 282 | ChangeNotificationCenter.defaultCenter().unregisterCacheDependancy(this, object); | ||
| 283 | } | ||
| 284 | |||
| 285 | /** Unregisters all of the callers dependancies. To be used sparingly as it may break unknown but required | ||
| 286 | * dependancies. Usually called when disposing the calling object.<BR> | ||
| 287 | * | ||
| 288 | * @see #registerCacheDependancy | ||
| 289 | * @see #unregisterCacheDependancy | ||
| 290 | */ | ||
| 291 | public void unregisterCacheDependancies() | ||
| 292 | { | ||
| 293 | ChangeNotificationCenter.defaultCenter().unregisterCacheDependancies(this); | ||
| 294 | } | ||
| 295 | |||
| 296 | /** Overridden to unregister all cache dependancies | ||
| 297 | */ | ||
| 298 | public void finalize() throws Throwable | ||
| 299 | { | ||
| 300 | ChangeNotificationCenter.defaultCenter().unregisterCacheDependancies(this); | ||
| 301 | |||
| 302 | super.finalize(); | ||
| 303 | } | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | {{/panel}} | ||
| 308 | |||
| 309 | Category:WebObjects |