KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > ObjectStore


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.access;
21
22 import java.io.Serializable JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30
31 import org.apache.cayenne.DataObject;
32 import org.apache.cayenne.DataRow;
33 import org.apache.cayenne.ObjectContext;
34 import org.apache.cayenne.ObjectId;
35 import org.apache.cayenne.PersistenceState;
36 import org.apache.cayenne.Persistent;
37 import org.apache.cayenne.access.ObjectDiff.ArcOperation;
38 import org.apache.cayenne.access.event.SnapshotEvent;
39 import org.apache.cayenne.access.event.SnapshotEventListener;
40 import org.apache.cayenne.graph.GraphChangeHandler;
41 import org.apache.cayenne.graph.GraphDiff;
42 import org.apache.cayenne.graph.GraphManager;
43 import org.apache.cayenne.graph.NodeCreateOperation;
44 import org.apache.cayenne.graph.NodeDeleteOperation;
45 import org.apache.cayenne.graph.NodeDiff;
46 import org.apache.cayenne.map.DataMap;
47 import org.apache.cayenne.map.DbEntity;
48 import org.apache.cayenne.map.ObjEntity;
49 import org.apache.cayenne.map.Procedure;
50 import org.apache.cayenne.query.ObjectIdQuery;
51 import org.apache.cayenne.query.PrefetchTreeNode;
52 import org.apache.cayenne.query.QueryMetadata;
53 import org.apache.cayenne.query.RefreshQuery;
54 import org.apache.cayenne.query.SQLResultSetMapping;
55 import org.apache.cayenne.reflect.AttributeProperty;
56 import org.apache.cayenne.reflect.ClassDescriptor;
57 import org.apache.cayenne.reflect.PropertyVisitor;
58 import org.apache.cayenne.reflect.ToManyProperty;
59 import org.apache.cayenne.reflect.ToOneProperty;
60 import org.apache.commons.collections.map.AbstractReferenceMap;
61 import org.apache.commons.collections.map.ReferenceMap;
62
63 /**
64  * ObjectStore stores objects using their ObjectId as a key. It works as a dedicated
65  * object cache for a DataContext. Users rarely need to access ObjectStore directly, as
66  * DataContext serves as a facade, providing cover methods for most ObjectStore
67  * operations.
68  *
69  * @since 1.0
70  * @author Andrus Adamchik
71  */

72 // Synchronization Note: There is often a need to do double synchronize on an ObjectStore
73
// and an underlying DataRowCache. To avoid deadlocks, Cayenne consistently follows the
74
// policy of locking an ObjectStore first, and then locking DataRowStore. This pattern
75
// must be followed in any new related developments.
76
public class ObjectStore implements Serializable JavaDoc, SnapshotEventListener, GraphManager {
77
78     /**
79      * Factory method to create default Map for storing registered objects.
80      *
81      * @since 3.0
82      * @return a map with hard referenced keys and weak referenced values.
83      */

84     static Map JavaDoc createObjectMap() {
85         return new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK);
86     }
87
88     protected Map JavaDoc objectMap;
89     protected Map JavaDoc changes;
90
91     // a sequential id used to tag GraphDiffs so that they can later be sorted in the
92
// original creation order
93
int currentDiffId;
94
95     /**
96      * Stores a reference to the DataRowStore.
97      * <p>
98      * <i>Serialization note: </i> It is up to the owner of this ObjectStore to initialize
99      * DataRowStore after deserialization of this object. ObjectStore will not know how to
100      * restore the DataRowStore by itself.
101      * </p>
102      */

103     protected transient DataRowStore dataRowCache;
104
105     // used to avoid incorrect on-demand DataRowStore initialization after deserialization
106
private boolean dataRowCacheSet;
107
108     /**
109      * The DataContext that owns this ObjectStore.
110      */

111     protected DataContext context;
112
113     public ObjectStore() {
114         this(null);
115     }
116
117     public ObjectStore(DataRowStore dataRowCache) {
118         this(dataRowCache, null);
119     }
120
121     /**
122      * Creates an ObjectStore with {@link DataRowStore} and a map to use for storing
123      * registered objects. Passed map doesn't require any special synchronization
124      * behavior, as ObjectStore is synchronized itself.
125      *
126      * @since 3.0
127      */

128     public ObjectStore(DataRowStore dataRowCache, Map JavaDoc objectMap) {
129         setDataRowCache(dataRowCache);
130         this.objectMap = objectMap != null ? objectMap : ObjectStore.createObjectMap();
131         this.changes = new HashMap JavaDoc();
132     }
133
134     /**
135      * Registers object change.
136      *
137      * @since 1.2
138      */

139     synchronized ObjectDiff registerDiff(Object JavaDoc nodeId, NodeDiff diff) {
140
141         if (diff != null) {
142             diff.setDiffId(++currentDiffId);
143         }
144
145         ObjectDiff objectDiff = (ObjectDiff) changes.get(nodeId);
146
147         if (objectDiff == null) {
148
149             Persistent object = (Persistent) objectMap.get(nodeId);
150             if (object.getPersistenceState() == PersistenceState.COMMITTED) {
151                 object.setPersistenceState(PersistenceState.MODIFIED);
152
153                 // TODO: andrus 3/23/2006 snapshot versions are obsolete, but there is no
154
// replacement yet, so we still need to handle them...
155
if (object instanceof DataObject) {
156
157                     DataObject dataObject = (DataObject) object;
158                     DataRow snapshot = getCachedSnapshot((ObjectId) nodeId);
159
160                     if (snapshot != null
161                             && snapshot.getVersion() != dataObject.getSnapshotVersion()) {
162                         DataContextDelegate delegate = context.nonNullDelegate();
163                         if (delegate.shouldMergeChanges(dataObject, snapshot)) {
164                             ClassDescriptor descriptor = context
165                                     .getEntityResolver()
166                                     .getClassDescriptor(
167                                             ((ObjectId) nodeId).getEntityName());
168                             DataRowUtils.forceMergeWithSnapshot(
169                                     context,
170                                     descriptor,
171                                     dataObject,
172                                     snapshot);
173                             dataObject.setSnapshotVersion(snapshot.getVersion());
174                             delegate.finishedMergeChanges(dataObject);
175                         }
176                     }
177                 }
178             }
179
180             objectDiff = new ObjectDiff(object);
181             objectDiff.setDiffId(++currentDiffId);
182             changes.put(nodeId, objectDiff);
183         }
184
185         if (diff != null) {
186             objectDiff.addDiff(diff);
187         }
188
189         return objectDiff;
190     }
191
192     /**
193      * Returns a number of objects currently registered with this ObjectStore.
194      *
195      * @since 1.2
196      */

197     public int registeredObjectsCount() {
198         return objectMap.size();
199     }
200
201     /**
202      * Returns a number of query results cached by this object store. Note that each
203      * result is a list and can possibly contain a large number of entries.
204      *
205      * @since 1.2
206      * @deprecated since 3.0. See {@link DataContext#getQueryCache()}.
207      */

208     public int cachedQueriesCount() {
209         return context != null && context.queryCache != null
210                 ? context.queryCache.size()
211                 : 0;
212     }
213
214     /**
215      * Returns a DataRowStore associated with this ObjectStore.
216      */

217     public DataRowStore getDataRowCache() {
218
219         // perform deferred initialization...
220

221         // Andrus, 11/7/2005 - potential problem with on-demand deferred initialization is
222
// that deserialized context won't receive any events... which maybe ok, since it
223
// didn't while it was stored in serialized form.
224
if (dataRowCache == null && context != null && dataRowCacheSet) {
225             synchronized (this) {
226                 if (dataRowCache == null) {
227                     DataDomain domain = context.getParentDataDomain();
228                     if (domain != null) {
229                         setDataRowCache(domain.getSharedSnapshotCache());
230                     }
231                 }
232             }
233         }
234
235         return dataRowCache;
236     }
237
238     /**
239      * Sets parent DataRowStore. Registers to receive SnapshotEvents if the cache is
240      * configured to allow ObjectStores to receive such events.
241      */

242     // note that as of 1.2, ObjectStore does not access DataRowStore directly when
243
// retrieving snapshots. Instead it sends a query via the DataContext's channel so
244
// that every element in the channel chain could intercept snapshot requests
245
public void setDataRowCache(DataRowStore dataRowCache) {
246         if (dataRowCache == this.dataRowCache) {
247             return;
248         }
249
250         if (this.dataRowCache != null && dataRowCache.getEventManager() != null) {
251             dataRowCache.getEventManager().removeListener(
252                     this,
253                     this.dataRowCache.getSnapshotEventSubject());
254         }
255
256         this.dataRowCache = dataRowCache;
257
258         if (dataRowCache != null && dataRowCache.getEventManager() != null) {
259             // setting itself as non-blocking listener,
260
// since event sending thread will likely be locking sender's
261
// ObjectStore and snapshot cache itself.
262
dataRowCache.getEventManager().addNonBlockingListener(
263                     this,
264                     "snapshotsChanged",
265                     SnapshotEvent.class,
266                     dataRowCache.getSnapshotEventSubject(),
267                     dataRowCache);
268         }
269
270         dataRowCacheSet = dataRowCache != null;
271     }
272
273     /**
274      * Invalidates a collection of DataObjects. Changes objects state to HOLLOW.
275      *
276      * @deprecated since 3.0, use {@link DataContext#invalidateObjects(Collection)} or
277      * {@link RefreshQuery}.
278      */

279     public synchronized void objectsInvalidated(Collection JavaDoc objects) {
280         if (context != null) {
281             context.invalidateObjects(objects);
282         }
283     }
284
285     /**
286      * Evicts a collection of DataObjects from the ObjectStore, invalidates the underlying
287      * cache snapshots. Changes objects state to TRANSIENT. This method can be used for
288      * manual cleanup of Cayenne cache.
289      *
290      * @see #objectsInvalidated(Collection)
291      */

292     // this method is exactly the same as "objectsInvalidated", only additionally it
293
// throws out registered objects
294
public synchronized void objectsUnregistered(Collection JavaDoc objects) {
295         if (objects.isEmpty()) {
296             return;
297         }
298
299         Collection JavaDoc ids = new ArrayList JavaDoc(objects.size());
300
301         Iterator JavaDoc it = objects.iterator();
302         while (it.hasNext()) {
303             Persistent object = (Persistent) it.next();
304
305             ObjectId id = object.getObjectId();
306
307             // remove object but not snapshot
308
objectMap.remove(id);
309             changes.remove(id);
310             ids.add(id);
311
312             object.setObjectContext(null);
313             object.setObjectId(null);
314             object.setPersistenceState(PersistenceState.TRANSIENT);
315         }
316
317         // TODO, andrus 3/28/2006 - DRC is null in nested contexts... implement
318
// propagation of unregister operation through the stack ... or do the opposite
319
// and keep unregister local even for non-nested DC?
320
if (getDataRowCache() != null) {
321             // send an event for removed snapshots
322
getDataRowCache().processSnapshotChanges(
323                     this,
324                     Collections.EMPTY_MAP,
325                     Collections.EMPTY_LIST,
326                     ids,
327                     Collections.EMPTY_LIST);
328         }
329     }
330
331     /**
332      * Reverts changes to all stored uncomitted objects.
333      *
334      * @since 1.1
335      */

336     public synchronized void objectsRolledBack() {
337         Iterator JavaDoc it = getObjectIterator();
338
339         // collect candidates
340
while (it.hasNext()) {
341             Persistent object = (Persistent) it.next();
342             int objectState = object.getPersistenceState();
343             switch (objectState) {
344                 case PersistenceState.NEW:
345                     it.remove();
346
347                     object.setObjectContext(null);
348                     object.setObjectId(null);
349                     object.setPersistenceState(PersistenceState.TRANSIENT);
350                     break;
351                 case PersistenceState.DELETED:
352                     // Do the same as for modified... deleted is only a persistence state,
353
// so
354
// rolling the object back will set the state to committed
355
case PersistenceState.MODIFIED:
356                     // this will clean any modifications and defer refresh from snapshot
357
// till the next object accessor is called
358
object.setPersistenceState(PersistenceState.HOLLOW);
359                     break;
360                 default:
361                     // Transient, committed and hollow need no handling
362
break;
363             }
364         }
365
366         // reset changes ... using new HashMap to allow event listeners to analyze the
367
// original changes map after the rollback
368
this.changes = new HashMap JavaDoc();
369     }
370
371     /**
372      * Updates snapshots in the underlying DataRowStore. If <code>refresh</code> is
373      * true, all snapshots in <code>snapshots</code> will be loaded into DataRowStore,
374      * regardless of the existing cache state. If <code>refresh</code> is false, only
375      * missing snapshots are loaded. This method is normally called internally by the
376      * DataContext owning the ObjectStore to update the caches after a select query.
377      *
378      * @param objects a list of object whose snapshots need to be updated.
379      * @param snapshots a list of snapshots. Must be of the same length and use the same
380      * order as <code>objects</code> list.
381      * @param refresh controls whether existing cached snapshots should be replaced with
382      * the new ones.
383      * @since 1.1
384      */

385     // TODO:, andrus 5/25/2006 - mark as deprecated after 1.2 - this method is no longer
386
// used.
387
public void snapshotsUpdatedForObjects(List JavaDoc objects, List JavaDoc snapshots, boolean refresh) {
388         DataRowStore cache = getDataRowCache();
389         if (cache != null) {
390             synchronized (this) {
391                 cache.snapshotsUpdatedForObjects(objects, snapshots, refresh);
392             }
393         }
394     }
395
396     /**
397      * Builds and returns GraphDiff reflecting all uncommitted object changes.
398      *
399      * @since 1.2
400      */

401     ObjectStoreGraphDiff getChanges() {
402         return new ObjectStoreGraphDiff(this);
403     }
404
405     /**
406      * Returns internal changes map.
407      *
408      * @since 1.2
409      */

410     Map JavaDoc getChangesByObjectId() {
411         return changes;
412     }
413
414     /**
415      * @since 1.2
416      */

417     void postprocessAfterPhantomCommit() {
418
419         Iterator JavaDoc it = changes.keySet().iterator();
420         while (it.hasNext()) {
421             ObjectId id = (ObjectId) it.next();
422
423             Persistent object = (Persistent) objectMap.get(id);
424
425             // assume that no new or deleted objects are present (as otherwise commit
426
// wouldn't have been phantom).
427
object.setPersistenceState(PersistenceState.COMMITTED);
428         }
429
430         // clear caches
431
this.changes.clear();
432     }
433
434     /**
435      * Internal unsynchronized method to process objects state after commit.
436      *
437      * @since 1.2
438      */

439     void postprocessAfterCommit(GraphDiff parentChanges) {
440
441         Iterator JavaDoc entries = objectMap.entrySet().iterator();
442
443         // have to scan through all entries
444
while (entries.hasNext()) {
445             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
446
447             Persistent object = (Persistent) entry.getValue();
448
449             switch (object.getPersistenceState()) {
450                 case PersistenceState.DELETED:
451                     entries.remove();
452                     object.setObjectContext(null);
453                     object.setPersistenceState(PersistenceState.TRANSIENT);
454                     break;
455                 case PersistenceState.NEW:
456                 case PersistenceState.MODIFIED:
457                     object.setPersistenceState(PersistenceState.COMMITTED);
458                     break;
459             }
460         }
461
462         // re-register changed object ids
463
if (!parentChanges.isNoop()) {
464             parentChanges.apply(new GraphChangeHandler() {
465
466                 public void arcCreated(Object JavaDoc nodeId, Object JavaDoc targetNodeId, Object JavaDoc arcId) {
467                 }
468
469                 public void arcDeleted(Object JavaDoc nodeId, Object JavaDoc targetNodeId, Object JavaDoc arcId) {
470                 }
471
472                 public void nodeCreated(Object JavaDoc nodeId) {
473                 }
474
475                 public void nodeIdChanged(Object JavaDoc nodeId, Object JavaDoc newId) {
476                     processIdChange(nodeId, newId);
477                 }
478
479                 public void nodePropertyChanged(
480                         Object JavaDoc nodeId,
481                         String JavaDoc property,
482                         Object JavaDoc oldValue,
483                         Object JavaDoc newValue) {
484                 }
485
486                 public void nodeRemoved(Object JavaDoc nodeId) {
487                 }
488             });
489         }
490
491         // create new instance of changes map so that event listeners who stored the
492
// original diff don't get affected
493
this.changes = new HashMap JavaDoc();
494     }
495
496     /**
497      * Starts tracking the registration of new objects from this ObjectStore. Used in
498      * conjunction with unregisterNewObjects() to control garbage collection when an
499      * instance of ObjectStore is used over a longer time for batch processing.
500      *
501      * @deprecated since 3.0 as ObjectStore holds weak reference to unmodified objects and
502      * this feature is useless.
503      */

504     public synchronized void startTrackingNewObjects() {
505         // noop
506
}
507
508     /**
509      * Unregisters the newly registered DataObjects from this objectStore. Used in
510      * conjunction with startTrackingNewObjects() to control garbage collection when an
511      * instance of ObjectStore is used over a longer time for batch processing.
512      *
513      * @deprecated since 3.0 as ObjectStore holds weak reference to unmodified objects and
514      * this feature is useless.
515      */

516     public synchronized void unregisterNewObjects() {
517         // noop
518
}
519
520     /**
521      * Returns a snapshot for ObjectId from the underlying snapshot cache. If cache
522      * contains no snapshot, a null is returned.
523      *
524      * @since 1.1
525      */

526     public DataRow getCachedSnapshot(ObjectId oid) {
527
528         if (context != null && context.getChannel() != null) {
529             ObjectIdQuery query = new CachedSnapshotQuery(oid);
530             List JavaDoc results = context.getChannel().onQuery(context, query).firstList();
531             return results.isEmpty() ? null : (DataRow) results.get(0);
532         }
533         else {
534             return null;
535         }
536     }
537
538     /**
539      * Returns cached query results for a given query, or null if no results are cached.
540      * Note that ObjectStore will only lookup results in its local cache, and not the
541      * shared cache associated with the underlying DataRowStore.
542      *
543      * @since 1.1
544      * @deprecated since 3.0. See {@link DataContext#getQueryCache()}.
545      */

546     public synchronized List JavaDoc getCachedQueryResult(String JavaDoc name) {
547         return context != null && context.queryCache != null ? context.queryCache
548                 .get(new CacheQueryMetadata(name)) : null;
549     }
550
551     /**
552      * Caches a list of query results.
553      *
554      * @since 1.1
555      * @deprecated since 3.0. See {@link DataContext#getQueryCache()}.
556      */

557     public synchronized void cacheQueryResult(String JavaDoc name, List JavaDoc results) {
558         if (context != null) {
559             context.getQueryCache().put(new CacheQueryMetadata(name), results);
560         }
561     }
562
563     /**
564      * Returns a snapshot for ObjectId from the underlying snapshot cache. If cache
565      * contains no snapshot, it will attempt fetching it using provided QueryEngine. If
566      * fetch attempt fails or inconsistent data is returned, underlying cache will throw a
567      * CayenneRuntimeException.
568      *
569      * @since 1.2
570      */

571     public synchronized DataRow getSnapshot(ObjectId oid) {
572
573         if (context != null && context.getChannel() != null) {
574             ObjectIdQuery query = new ObjectIdQuery(oid, true, ObjectIdQuery.CACHE);
575             List JavaDoc results = context.getChannel().onQuery(context, query).firstList();
576             return results.isEmpty() ? null : (DataRow) results.get(0);
577         }
578         else {
579             return null;
580         }
581     }
582
583     /**
584      * Returns an iterator over the registered objects.
585      */

586     public synchronized Iterator JavaDoc getObjectIterator() {
587         return objectMap.values().iterator();
588     }
589
590     /**
591      * Returns <code>true</code> if there are any modified, deleted or new objects
592      * registered with this ObjectStore, <code>false</code> otherwise. This method will
593      * treat "phantom" modifications are real ones. I.e. if you "change" an object
594      * property to an equivalent value, this method will still think such object is
595      * modified. Phantom modifications are only detected and discarded during commit.
596      */

597     public synchronized boolean hasChanges() {
598         return !changes.isEmpty();
599     }
600
601     /**
602      * Return a subset of registered objects that are in a certian persistence state.
603      * Collection is returned by copy.
604      */

605     public synchronized List JavaDoc objectsInState(int state) {
606         List JavaDoc filteredObjects = new ArrayList JavaDoc();
607
608         Iterator JavaDoc it = objectMap.values().iterator();
609         while (it.hasNext()) {
610             Persistent nextObj = (Persistent) it.next();
611             if (nextObj.getPersistenceState() == state) {
612                 filteredObjects.add(nextObj);
613             }
614         }
615
616         return filteredObjects;
617     }
618
619     /**
620      * SnapshotEventListener implementation that processes snapshot change event, updating
621      * DataObjects that have the changes.
622      * <p>
623      * <i>Implementation note: </i> This method should not attempt to alter the underlying
624      * DataRowStore, since it is normally invoked *AFTER* the DataRowStore was modified as
625      * a result of some external interaction.
626      * </p>
627      *
628      * @since 1.1
629      */

630     public void snapshotsChanged(SnapshotEvent event) {
631         // filter events that we should not process
632
if (event.getPostedBy() != this && event.getSource() == this.getDataRowCache()) {
633             processSnapshotEvent(event);
634         }
635     }
636
637     /**
638      * @since 1.2
639      */

640     synchronized void processSnapshotEvent(SnapshotEvent event) {
641
642         Map JavaDoc modifiedDiffs = event.getModifiedDiffs();
643         if (modifiedDiffs != null && !modifiedDiffs.isEmpty()) {
644             Iterator JavaDoc oids = modifiedDiffs.entrySet().iterator();
645
646             while (oids.hasNext()) {
647                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) oids.next();
648                 processUpdatedSnapshot(entry.getKey(), (DataRow) entry.getValue());
649             }
650         }
651
652         Collection JavaDoc deletedIDs = event.getDeletedIds();
653         if (deletedIDs != null && !deletedIDs.isEmpty()) {
654             Iterator JavaDoc it = deletedIDs.iterator();
655             while (it.hasNext()) {
656                 processDeletedID(it.next());
657             }
658         }
659
660         processInvalidatedIDs(event.getInvalidatedIds());
661         processIndirectlyModifiedIDs(event.getIndirectlyModifiedIds());
662
663         // TODO: andrus, 3/28/2006 - 'SnapshotEventDecorator' serves as a bridge (or
664
// rather a noop wrapper) between old snapshot events and new GraphEvents. Once
665
// SnapshotEvents are replaced with GraphEvents (in 2.0) we won't need it
666
GraphDiff diff = new SnapshotEventDecorator(event);
667
668         ObjectContext originatingContext = (event.getPostedBy() instanceof ObjectContext)
669                 ? (ObjectContext) event.getPostedBy()
670                 : null;
671         context.fireDataChannelChanged(originatingContext, diff);
672     }
673
674     /**
675      * Initializes object with data from cache or from the database, if this object is not
676      * fully resolved.
677      *
678      * @since 1.1
679      * @deprecated since 3.0 use
680      * {@link ObjectContext#prepareForAccess(Persistent, String, boolean)}.
681      */

682     public void resolveHollow(Persistent object) {
683         context.prepareForAccess(object, null, false);
684     }
685
686     void processIdChange(Object JavaDoc nodeId, Object JavaDoc newId) {
687         Persistent object = (Persistent) objectMap.remove(nodeId);
688
689         if (object != null) {
690             object.setObjectId((ObjectId) newId);
691             objectMap.put(newId, object);
692
693             Object JavaDoc change = changes.remove(nodeId);
694             if (change != null) {
695                 changes.put(newId, change);
696             }
697         }
698     }
699
700     /**
701      * Requires external synchronization.
702      *
703      * @since 1.2
704      */

705     void processDeletedID(Object JavaDoc nodeId) {
706
707         // access object map directly - the method should be called in a synchronized
708
// context...
709
Persistent object = (Persistent) objectMap.get(nodeId);
710
711         if (object != null) {
712
713             DataObject dataObject = (object instanceof DataObject)
714                     ? (DataObject) object
715                     : null;
716
717             DataContextDelegate delegate;
718
719             switch (object.getPersistenceState()) {
720                 case PersistenceState.COMMITTED:
721                 case PersistenceState.HOLLOW:
722                 case PersistenceState.DELETED:
723
724                     // consult delegate
725
delegate = context.nonNullDelegate();
726
727                     if (dataObject == null || delegate.shouldProcessDelete(dataObject)) {
728                         objectMap.remove(nodeId);
729                         changes.remove(nodeId);
730
731                         // setting DataContext to null will also set
732
// state to transient
733
object.setObjectContext(null);
734
735                         if (dataObject != null) {
736                             delegate.finishedProcessDelete(dataObject);
737                         }
738                     }
739
740                     break;
741
742                 case PersistenceState.MODIFIED:
743
744                     // consult delegate
745
delegate = context.nonNullDelegate();
746                     if (dataObject != null && delegate.shouldProcessDelete(dataObject)) {
747                         object.setPersistenceState(PersistenceState.NEW);
748                         changes.remove(nodeId);
749                         registerNode(nodeId, object);
750                         nodeCreated(nodeId);
751                         if (dataObject != null) {
752                             delegate.finishedProcessDelete(dataObject);
753                         }
754                     }
755
756                     break;
757             }
758         }
759     }
760
761     /**
762      * @since 1.1
763      */

764     void processInvalidatedIDs(Collection JavaDoc invalidatedIDs) {
765         if (invalidatedIDs != null && !invalidatedIDs.isEmpty()) {
766             Iterator JavaDoc it = invalidatedIDs.iterator();
767             while (it.hasNext()) {
768                 ObjectId oid = (ObjectId) it.next();
769                 DataObject object = (DataObject) getNode(oid);
770
771                 if (object == null) {
772                     continue;
773                 }
774
775                 // TODO: refactor "switch" to avoid code duplication
776

777                 switch (object.getPersistenceState()) {
778                     case PersistenceState.COMMITTED:
779                         object.setPersistenceState(PersistenceState.HOLLOW);
780                         break;
781                     case PersistenceState.MODIFIED:
782                         DataContext context = (DataContext) object.getObjectContext();
783                         DataRow diff = getSnapshot(oid);
784                         // consult delegate if it exists
785
DataContextDelegate delegate = context.nonNullDelegate();
786                         if (delegate.shouldMergeChanges(object, diff)) {
787                             ClassDescriptor descriptor = context
788                                     .getEntityResolver()
789                                     .getClassDescriptor(oid.getEntityName());
790                             DataRowUtils.forceMergeWithSnapshot(
791                                     context,
792                                     descriptor,
793                                     object,
794                                     diff);
795                             delegate.finishedMergeChanges(object);
796                         }
797
798                     case PersistenceState.HOLLOW:
799                         // do nothing
800
break;
801
802                     case PersistenceState.DELETED:
803                         // TODO: Do nothing? Or treat as merged?
804
break;
805                 }
806             }
807         }
808     }
809
810     /**
811      * Requires external synchronization.
812      *
813      * @since 1.1
814      */

815     void processIndirectlyModifiedIDs(Collection JavaDoc indirectlyModifiedIDs) {
816         Iterator JavaDoc indirectlyModifiedIt = indirectlyModifiedIDs.iterator();
817         while (indirectlyModifiedIt.hasNext()) {
818             ObjectId oid = (ObjectId) indirectlyModifiedIt.next();
819
820             // access object map directly - the method should be called in a synchronized
821
// context...
822
final DataObject object = (DataObject) objectMap.get(oid);
823
824             if (object == null
825                     || object.getPersistenceState() != PersistenceState.COMMITTED) {
826                 continue;
827             }
828
829             // for now break all "independent" object relationships...
830
// in the future we may want to be more precise and go after modified
831
// relationships only, or even process updated lists without invalidating...
832

833             DataContextDelegate delegate = context.nonNullDelegate();
834
835             if (delegate.shouldMergeChanges(object, null)) {
836
837                 ClassDescriptor descriptor = context
838                         .getEntityResolver()
839                         .getClassDescriptor(oid.getEntityName());
840                 descriptor.visitProperties(new PropertyVisitor() {
841
842                     public boolean visitToMany(ToManyProperty property) {
843                         property.invalidate(object);
844                         return true;
845                     }
846
847                     public boolean visitToOne(ToOneProperty property) {
848                         if (property
849                                 .getRelationship()
850                                 .isSourceIndependentFromTargetChange()) {
851                             property.invalidate(object);
852                         }
853                         return true;
854                     }
855
856                     public boolean visitAttribute(AttributeProperty property) {
857                         return true;
858                     }
859                 });
860
861                 delegate.finishedProcessDelete(object);
862             }
863         }
864     }
865
866     /**
867      * Requires external synchronization.
868      *
869      * @since 1.1
870      */

871     void processUpdatedSnapshot(Object JavaDoc nodeId, DataRow diff) {
872
873         // access object map directly - the method should be called ina synchronized
874
// context...
875
DataObject object = (DataObject) objectMap.get(nodeId);
876
877         // no object, or HOLLOW object require no processing
878
if (object != null) {
879
880             int state = object.getPersistenceState();
881             if (state != PersistenceState.HOLLOW) {
882
883                 // perform same steps as resolveHollow()
884
if (state == PersistenceState.COMMITTED) {
885                     // consult delegate if it exists
886
DataContextDelegate delegate = context.nonNullDelegate();
887                     if (delegate.shouldMergeChanges(object, diff)) {
888                         ClassDescriptor descriptor = context
889                                 .getEntityResolver()
890                                 .getClassDescriptor(((ObjectId) nodeId).getEntityName());
891
892                         // TODO: andrus, 5/26/2006 - call to 'getSnapshot' is expensive,
893
// however my attempts to merge the 'diff' instead of snapshot
894
// via 'refreshObjectWithSnapshot' resulted in even worse
895
// performance.
896
// This sounds counterintuitive (Not sure if this is some HotSpot
897
// related glitch)... still keeping the old algorithm here until
898
// we
899
// switch from snapshot events to GraphEvents and all this code
900
// becomes obsolete.
901
DataRow snapshot = getSnapshot(object.getObjectId());
902
903                         DataRowUtils.refreshObjectWithSnapshot(
904                                 descriptor,
905                                 object,
906                                 snapshot,
907                                 true);
908                         delegate.finishedMergeChanges(object);
909                     }
910                 }
911                 // merge modified and deleted
912
else if (state == PersistenceState.DELETED
913                         || state == PersistenceState.MODIFIED) {
914
915                     // consult delegate if it exists
916
DataContextDelegate delegate = context.nonNullDelegate();
917                     if (delegate.shouldMergeChanges(object, diff)) {
918                         ClassDescriptor descriptor = context
919                                 .getEntityResolver()
920                                 .getClassDescriptor(((ObjectId) nodeId).getEntityName());
921                         DataRowUtils.forceMergeWithSnapshot(
922                                 context,
923                                 descriptor,
924                                 object,
925                                 diff);
926                         delegate.finishedMergeChanges(object);
927                     }
928                 }
929             }
930         }
931     }
932
933     /**
934      * @since 1.2
935      */

936     public DataContext getContext() {
937         return context;
938     }
939
940     /**
941      * @since 1.2
942      */

943     public void setContext(DataContext context) {
944         this.context = context;
945     }
946
947     // *********** GraphManager Methods ********
948
// =========================================
949

950     /**
951      * Returns a registered DataObject or null of no object exists for the ObjectId.
952      *
953      * @since 1.2
954      */

955     public synchronized Object JavaDoc getNode(Object JavaDoc nodeId) {
956         return objectMap.get(nodeId);
957     }
958
959     // non-synchronized version of getNode for private use
960
final Object JavaDoc getNodeNoSync(Object JavaDoc nodeId) {
961         return objectMap.get(nodeId);
962     }
963
964     /**
965      * Returns all registered DataObjects. List is returned by copy and can be modified by
966      * the caller.
967      *
968      * @since 1.2
969      */

970     public synchronized Collection JavaDoc registeredNodes() {
971         return new ArrayList JavaDoc(objectMap.values());
972     }
973
974     /**
975      * @since 1.2
976      */

977     public synchronized void registerNode(Object JavaDoc nodeId, Object JavaDoc nodeObject) {
978         objectMap.put(nodeId, nodeObject);
979     }
980
981     /**
982      * @since 1.2
983      */

984     public synchronized Object JavaDoc unregisterNode(Object JavaDoc nodeId) {
985         Object JavaDoc object = getNode(nodeId);
986         if (object != null) {
987             objectsUnregistered(Collections.singleton(object));
988         }
989
990         return object;
991     }
992
993     /**
994      * Does nothing.
995      *
996      * @since 1.2
997      */

998     public void nodeIdChanged(Object JavaDoc nodeId, Object JavaDoc newId) {
999         throw new UnsupportedOperationException JavaDoc("nodeIdChanged");
1000    }
1001
1002    /**
1003     * @since 1.2
1004     */

1005    public void nodeCreated(Object JavaDoc nodeId) {
1006        registerDiff(nodeId, new NodeCreateOperation(nodeId));
1007    }
1008
1009    /**
1010     * @since 1.2
1011     */

1012    public void nodeRemoved(Object JavaDoc nodeId) {
1013        registerDiff(nodeId, new NodeDeleteOperation(nodeId));
1014    }
1015
1016    /**
1017     * Records dirty object snapshot.
1018     *
1019     * @since 1.2
1020     */

1021    public void nodePropertyChanged(
1022            Object JavaDoc nodeId,
1023            String JavaDoc property,
1024            Object JavaDoc oldValue,
1025            Object JavaDoc newValue) {
1026
1027        registerDiff(nodeId, null);
1028    }
1029
1030    /**
1031     * @since 1.2
1032     */

1033    public void arcCreated(Object JavaDoc nodeId, Object JavaDoc targetNodeId, Object JavaDoc arcId) {
1034        registerDiff(nodeId, new ArcOperation(
1035                nodeId,
1036                targetNodeId,
1037                arcId.toString(),
1038                false));
1039    }
1040
1041    /**
1042     * @since 1.2
1043     */

1044    public void arcDeleted(Object JavaDoc nodeId, Object JavaDoc targetNodeId, Object JavaDoc arcId) {
1045        registerDiff(nodeId, new ArcOperation(
1046                nodeId,
1047                targetNodeId,
1048                arcId.toString(),
1049                true));
1050    }
1051
1052    // an ObjectIdQuery optimized for retrieval of multiple snapshots - it can be reset
1053
// with the new id
1054
final class CachedSnapshotQuery extends ObjectIdQuery {
1055
1056        CachedSnapshotQuery(ObjectId oid) {
1057            super(oid, true, ObjectIdQuery.CACHE_NOREFRESH);
1058        }
1059
1060        void resetId(ObjectId oid) {
1061            this.objectId = oid;
1062            this.replacementQuery = null;
1063        }
1064    }
1065
1066    class SnapshotEventDecorator implements GraphDiff {
1067
1068        SnapshotEvent event;
1069
1070        SnapshotEventDecorator(SnapshotEvent event) {
1071            this.event = event;
1072        }
1073
1074        SnapshotEvent getEvent() {
1075            return event;
1076        }
1077
1078        public void apply(GraphChangeHandler handler) {
1079            throw new UnsupportedOperationException JavaDoc();
1080        }
1081
1082        public boolean isNoop() {
1083            throw new UnsupportedOperationException JavaDoc();
1084        }
1085
1086        public void undo(GraphChangeHandler handler) {
1087            throw new UnsupportedOperationException JavaDoc();
1088        }
1089    }
1090
1091    final class CacheQueryMetadata implements QueryMetadata {
1092
1093        private String JavaDoc cacheKey;
1094
1095        CacheQueryMetadata(String JavaDoc cacheKey) {
1096            this.cacheKey = cacheKey;
1097        }
1098
1099        public String JavaDoc getCacheKey() {
1100            return cacheKey;
1101        }
1102        
1103        public SQLResultSetMapping getResultSetMapping() {
1104            return null;
1105        }
1106
1107        public String JavaDoc[] getCacheGroups() {
1108            return null;
1109        }
1110
1111        public String JavaDoc getCachePolicy() {
1112            return null;
1113        }
1114
1115        public DataMap getDataMap() {
1116            return null;
1117        }
1118
1119        public DbEntity getDbEntity() {
1120            return null;
1121        }
1122
1123        public int getFetchLimit() {
1124            return 0;
1125        }
1126
1127        public int getFetchStartIndex() {
1128            return 0;
1129        }
1130
1131        public ObjEntity getObjEntity() {
1132            return null;
1133        }
1134
1135        public ClassDescriptor getClassDescriptor() {
1136            return null;
1137        }
1138
1139        public int getPageSize() {
1140            return 0;
1141        }
1142
1143        public PrefetchTreeNode getPrefetchTree() {
1144            return null;
1145        }
1146
1147        public Procedure getProcedure() {
1148            return null;
1149        }
1150
1151        public boolean isFetchingDataRows() {
1152            return false;
1153        }
1154
1155        public boolean isRefreshingObjects() {
1156            return false;
1157        }
1158
1159        public boolean isResolvingInherited() {
1160            return false;
1161        }
1162    }
1163}
1164
Popular Tags