Last modified by Pascal Robert on 2007/09/03 13:57

Hide last authors
Pascal Robert 6.1 1 == EO Change Notifications ==
Quinton Dolan 2.1 2
Pascal Robert 6.1 3 === Pierre Bernard ===
Quinton Dolan 2.1 4
5 To invalidate cached versions of derived values, you might want to use this:
6
Pascal Robert 6.1 7 {{code}}
Quinton Dolan 2.1 8
Pascal Robert 6.1 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()
Quinton Dolan 2.1 61 {
Pascal Robert 6.1 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)
Quinton Dolan 2.1 86 {
Pascal Robert 6.1 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 }
Quinton Dolan 2.1 96 }
Pascal Robert 6.1 97
98 NSArray invalidated = (NSArray) notification.userInfo().objectForKey(EOObjectStore.InvalidatedKey);
99
100 if (invalidated != null)
Quinton Dolan 2.1 101 {
Pascal Robert 6.1 102 int count = invalidated.count();
103
104 for (int i = 0; i < count; i++)
Quinton Dolan 2.1 105 {
Pascal Robert 6.1 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);
Quinton Dolan 2.1 110 }
Pascal Robert 6.1 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++)
Quinton Dolan 2.1 120 {
Pascal Robert 6.1 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);
Quinton Dolan 2.1 124 }
125 }
Pascal Robert 6.1 126 }
127
128 /** Method to be called when one creates a cached value that depends on the attributes of a given object.
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.
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.
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.
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 == 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.
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.
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.
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.
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.
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.
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.
261 *
262 * N.B. Extend the dispose() method to unregister any dependancy you might be registered for.
263 *
Quinton Dolan 2.1 264 * @param object the object to watch
265 * @see #unregisterCacheDependancy
Pascal Robert 6.1 266 * @see #clearCachedValues
Quinton Dolan 2.1 267 */
Pascal Robert 6.1 268 protected void registerCacheDependancy(EOEnterpriseObject object)
Quinton Dolan 2.1 269 {
Pascal Robert 6.1 270 ChangeNotificationCenter.defaultCenter().registerCacheDependancy(this, object);
Quinton Dolan 2.1 271 }
Pascal Robert 6.1 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.
275 *
Quinton Dolan 2.1 276 * @param object the object to watch
277 * @see #registerCacheDependancy
Pascal Robert 6.1 278 * @see #clearCachedValues
Quinton Dolan 2.1 279 */
Pascal Robert 6.1 280 protected void unregisterCacheDependancy(EOEnterpriseObject object)
Quinton Dolan 2.1 281 {
Pascal Robert 6.1 282 ChangeNotificationCenter.defaultCenter().unregisterCacheDependancy(this, object);
Quinton Dolan 2.1 283 }
Pascal Robert 6.1 284
Quinton Dolan 2.1 285 /** Unregisters all of the callers dependancies. To be used sparingly as it may break unknown but required
Pascal Robert 6.1 286 * dependancies. Usually called when disposing the calling object.
287 *
Quinton Dolan 2.1 288 * @see #registerCacheDependancy
289 * @see #unregisterCacheDependancy
290 */
Pascal Robert 6.1 291 public void unregisterCacheDependancies()
Quinton Dolan 2.1 292 {
Pascal Robert 6.1 293 ChangeNotificationCenter.defaultCenter().unregisterCacheDependancies(this);
Quinton Dolan 2.1 294 }
Pascal Robert 6.1 295
296 /** Overridden to unregister all cache dependancies
Quinton Dolan 2.1 297 */
Pascal Robert 6.1 298 public void finalize() throws Throwable
Quinton Dolan 2.1 299 {
Pascal Robert 6.1 300 ChangeNotificationCenter.defaultCenter().unregisterCacheDependancies(this);
301
302 super.finalize();
Quinton Dolan 2.1 303 }
304 }
Pascal Robert 6.1 305 }
Quinton Dolan 2.1 306
Pascal Robert 6.1 307 {{/code}}