KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > sessions > MergeManager


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.internal.sessions;
23
24 import java.util.*;
25 import oracle.toplink.essentials.descriptors.VersionLockingPolicy;
26 import oracle.toplink.essentials.mappings.*;
27 import oracle.toplink.essentials.exceptions.*;
28 import oracle.toplink.essentials.internal.helper.*;
29 import oracle.toplink.essentials.internal.helper.linkedlist.LinkedNode;
30 import oracle.toplink.essentials.internal.queryframework.ContainerPolicy;
31 import oracle.toplink.essentials.internal.identitymaps.*;
32 import oracle.toplink.essentials.logging.SessionLog;
33 import oracle.toplink.essentials.sessions.SessionProfiler;
34 import oracle.toplink.essentials.descriptors.ClassDescriptor;
35 import oracle.toplink.essentials.internal.descriptors.ObjectBuilder;
36 import oracle.toplink.essentials.internal.descriptors.OptimisticLockingPolicy;
37
38 /**
39  * <p><b>Purpose</b>:
40  * Used to manage the merge of two objects in a unit of work.
41  *
42  * @author James Sutherland
43  * @since TOPLink/Java 1.1
44  */

45 public class MergeManager {
46
47     /** The unit of work merging for. */
48     protected AbstractSession session;
49
50     /** Used only while refreshing objects on remote session */
51     protected IdentityHashtable objectDescriptors;
52
53     /** Used to unravle recursion. */
54     protected IdentityHashtable objectsAlreadyMerged;
55
56     /** Used to store the list of locks that this merge manager has acquired for this merge */
57     protected ArrayList acquiredLocks;
58
59     /** If this variable is not null then the mergemanager is waiting on a particular cacheKey */
60     protected CacheKey writeLockQueued;
61
62     /** Stores the node that holds this mergemanager within the WriteLocksManager queue */
63     protected LinkedNode queueNode;
64
65     /** Policy that determines merge type (i.e. merge is used for several usages). */
66     protected int mergePolicy;
67     protected static final int WORKING_COPY_INTO_ORIGINAL = 1;
68     protected static final int ORIGINAL_INTO_WORKING_COPY = 2;
69     protected static final int CLONE_INTO_WORKING_COPY = 3;
70     protected static final int WORKING_COPY_INTO_REMOTE = 4;
71     protected static final int REFRESH_REMOTE_OBJECT = 5;
72     protected static final int CHANGES_INTO_DISTRIBUTED_CACHE = 6;
73     protected static final int CLONE_WITH_REFS_INTO_WORKING_COPY = 7;
74     protected static final int WORKING_COPY_INTO_BACKUP = 9;
75
76     /** Policy that determines how the merge will cascade to its object's parts. */
77     protected int cascadePolicy;
78     public static final int NO_CASCADE = 1;
79     public static final int CASCADE_PRIVATE_PARTS = 2;
80     public static final int CASCADE_ALL_PARTS = 3;
81     public static final int CASCADE_BY_MAPPING = 4;
82     protected long systemTime = 0;// stored so that all objects merged by a merge manager can have the same readTime
83
public static boolean LOCK_ON_MERGE = true;
84
85     public MergeManager(AbstractSession session) {
86         this.session = session;
87         this.objectsAlreadyMerged = new IdentityHashtable();
88         this.cascadePolicy = CASCADE_ALL_PARTS;
89         this.mergePolicy = WORKING_COPY_INTO_ORIGINAL;
90         this.objectDescriptors = new IdentityHashtable();
91         this.acquiredLocks = new ArrayList();
92     }
93
94     /**
95      * Build and return an identity set for the specified container.
96      */

97     protected IdentityHashtable buildIdentitySet(Object JavaDoc container, ContainerPolicy containerPolicy, boolean keyByTarget) {
98         // find next power-of-2 size
99
IdentityHashtable result = new IdentityHashtable(containerPolicy.sizeFor(container) + 1);
100         for (Object JavaDoc iter = containerPolicy.iteratorFor(container); containerPolicy.hasNext(iter);) {
101             Object JavaDoc element = containerPolicy.next(iter, getSession());
102             if (keyByTarget) {
103                 result.put(getTargetVersionOfSourceObject(element), element);
104             } else {
105                 result.put(element, element);
106             }
107         }
108         return result;
109     }
110
111     /**
112      * Cascade all parts, this is the default for the merge.
113      */

114     public void cascadeAllParts() {
115         setCascadePolicy(CASCADE_ALL_PARTS);
116     }
117
118     /**
119      * Cascade private parts, this can be used to merge clone when using RMI.
120      */

121     public void cascadePrivateParts() {
122         setCascadePolicy(CASCADE_PRIVATE_PARTS);
123     }
124
125     /**
126      * Merge only direct parts, this can be used to merge clone when using RMI.
127      */

128     public void dontCascadeParts() {
129         setCascadePolicy(NO_CASCADE);
130     }
131
132     public ArrayList getAcquiredLocks() {
133         return this.acquiredLocks;
134     }
135
136     public int getCascadePolicy() {
137         return cascadePolicy;
138     }
139
140     protected int getMergePolicy() {
141         return mergePolicy;
142     }
143
144     public IdentityHashtable getObjectDescriptors() {
145         return objectDescriptors;
146     }
147
148     // cr 2855 changed visibility of following method
149
public IdentityHashtable getObjectsAlreadyMerged() {
150         return objectsAlreadyMerged;
151     }
152
153     public Object JavaDoc getObjectToMerge(Object JavaDoc sourceValue) {
154         if (shouldMergeOriginalIntoWorkingCopy()) {
155             return getTargetVersionOfSourceObject(sourceValue);
156         }
157
158         return sourceValue;
159     }
160
161     /**
162      * INTENRAL:
163      * Used to get the node that this merge manager is stored in, within the WriteLocksManager write lockers queue
164      */

165     public LinkedNode getQueueNode() {
166         return this.queueNode;
167     }
168
169     public AbstractSession getSession() {
170         return session;
171     }
172
173     /**
174      * Get the stored value of the current time. This method lazily initializes
175      * so that read times for the same merge manager can all be set to the same read time
176      */

177     public long getSystemTime() {
178         if (systemTime == 0) {
179             systemTime = System.currentTimeMillis();
180         }
181         return systemTime;
182     }
183
184     /**
185      * Return the coresponding value that should be assigned to the target object for the source object.
186      * This value must be local to the targets object space.
187      */

188     public Object JavaDoc getTargetVersionOfSourceObject(Object JavaDoc source) {
189         if (shouldMergeWorkingCopyIntoOriginal() || shouldMergeWorkingCopyIntoRemote()) {
190             // Target is in uow parent, or original instance for new object.
191
return ((UnitOfWorkImpl)getSession()).getOriginalVersionOfObject(source);
192         } else if (shouldMergeCloneIntoWorkingCopy() || shouldMergeOriginalIntoWorkingCopy() || shouldMergeCloneWithReferencesIntoWorkingCopy()) {
193             // Target is clone from uow.
194
//make sure we use the register for merge
195
//bug 3584343
196
return registerObjectForMergeCloneIntoWorkingCopy(source);
197         } else if (shouldRefreshRemoteObject()) {
198             // Target is in session's cache.
199
ClassDescriptor descriptor = getSession().getDescriptor(source);
200             Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(source, getSession());
201             return getSession().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, source.getClass(), descriptor, null);
202         }
203
204         throw ValidationException.invalidMergePolicy();
205     }
206
207     /**
208      * INTENRAL:
209      * Used to get the object that the merge manager is waiting on, in order to acquire locks
210      */

211     public CacheKey getWriteLockQueued() {
212         return this.writeLockQueued;
213     }
214
215     /**
216      * Recursively merge changes in the object dependent on the merge policy.
217      * The hastable is used to resolv recursion.
218      */

219     public Object JavaDoc mergeChanges(Object JavaDoc object, ObjectChangeSet objectChangeSet) throws ValidationException {
220         if (object == null) {
221             return object;
222         }
223
224         // Do not merged read-only objects in a unit of work.
225
if (getSession().isClassReadOnly(object.getClass())) {
226             return object;
227         }
228
229         // Means that object is either already merged or in the process of being merged.
230
if (getObjectsAlreadyMerged().containsKey(object)) {
231             return object;
232         }
233
234         // Put the object to be merged in the set.
235
getObjectsAlreadyMerged().put(object, object);
236
237         Object JavaDoc mergedObject;
238         if (shouldMergeWorkingCopyIntoOriginal()) {
239             mergedObject = mergeChangesOfWorkingCopyIntoOriginal(object, objectChangeSet);
240         } else if (shouldMergeCloneIntoWorkingCopy() || shouldMergeCloneWithReferencesIntoWorkingCopy()) {
241             mergedObject = mergeChangesOfCloneIntoWorkingCopy(object);
242         } else if (shouldMergeOriginalIntoWorkingCopy()) {
243             mergedObject = mergeChangesOfOriginalIntoWorkingCopy(object);
244         } else {
245             throw ValidationException.invalidMergePolicy();
246         }
247
248         return mergedObject;
249     }
250
251     /**
252      * INTERNAL:
253      * Merge the changes to all objects to session's cache.
254      */

255     public void mergeChangesFromChangeSet(UnitOfWorkChangeSet uowChangeSet) {
256         getSession().startOperationProfile(SessionProfiler.DistributedMerge);
257         // Ensure concurrency if cache isolation requires.
258
getSession().getIdentityMapAccessorInstance().acquireWriteLock();
259         getSession().log(SessionLog.FINER, SessionLog.PROPAGATION, "received_updates_from_remote_server");
260         getSession().getEventManager().preDistributedMergeUnitOfWorkChangeSet(uowChangeSet);
261
262         try {
263             // Iterate over each clone and let the object build merge to clones into the originals.
264
getSession().getIdentityMapAccessorInstance().getWriteLockManager().acquireRequiredLocks(this, uowChangeSet);
265             Enumeration objectChangeEnum = uowChangeSet.getAllChangeSets().keys();
266             while (objectChangeEnum.hasMoreElements()) {
267                 ObjectChangeSet objectChangeSet = (ObjectChangeSet)objectChangeEnum.nextElement();
268                 Object JavaDoc object = objectChangeSet.getTargetVersionOfSourceObject(getSession(), false);
269
270                 // Don't read the object here. If it is null then we won't merge it at this stage, unless it
271
// is being referenced which will force the load later
272
Object JavaDoc mergedObject = this.mergeChanges(object, objectChangeSet);
273
274                 // if mergedObject is null, it might be because objectChangeSet represents a new object and could not look it up
275
// Check the descriptor setting for this change set to see if the new object should be added to the cache.
276
if (mergedObject == null) {
277                     if (objectChangeSet.isNew()) {
278                         mergedObject = mergeNewObjectIntoCache(objectChangeSet);
279                     }
280                 }
281                 if (mergedObject == null) {
282                     getSession().incrementProfile(SessionProfiler.ChangeSetsNotProcessed);
283                 } else {
284                     getSession().incrementProfile(SessionProfiler.ChangeSetsProcessed);
285                 }
286             }
287             Enumeration deletedObjects = uowChangeSet.getDeletedObjects().elements();
288             while (deletedObjects.hasMoreElements()) {
289                 ObjectChangeSet changeSet = (ObjectChangeSet)deletedObjects.nextElement();
290                 changeSet.removeFromIdentityMap(getSession());
291                 getSession().incrementProfile(SessionProfiler.DeletedObject);
292             }
293         } catch (RuntimeException JavaDoc exception) {
294             getSession().handleException(exception);
295         } finally {
296             getSession().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(this);
297             getSession().getIdentityMapAccessorInstance().releaseWriteLock();
298             getSession().getEventManager().postDistributedMergeUnitOfWorkChangeSet(uowChangeSet);
299             getSession().endOperationProfile(SessionProfiler.DistributedMerge);
300         }
301     }
302
303     /**
304      * Merge the changes from the collection of a source object into the target using the backup as the diff.
305      * Return true if any changes occurred.
306      */

307     public boolean mergeChangesInCollection(Object JavaDoc source, Object JavaDoc target, Object JavaDoc backup, DatabaseMapping mapping) {
308         ContainerPolicy containerPolicy = mapping.getContainerPolicy();
309
310         // The vectors must be converted to identity sets to avoid dependency on #equals().
311
IdentityHashtable backupSet = buildIdentitySet(backup, containerPolicy, false);
312         IdentityHashtable sourceSet = null;
313         IdentityHashtable targetToSources = null;
314
315         // We need either a source or target-to-source set, depending on the merge type.
316
if (shouldMergeWorkingCopyIntoOriginal()) {
317             sourceSet = buildIdentitySet(source, containerPolicy, false);
318         } else {
319             targetToSources = buildIdentitySet(source, containerPolicy, true);
320         }
321
322         boolean changeOccured = false;
323
324         // If the backup is also the target, clone it; since it may change during the loop.
325
if (backup == target) {
326             backup = containerPolicy.cloneFor(backup);
327         }
328
329         // Handle removed elements.
330
for (Object JavaDoc backupIter = containerPolicy.iteratorFor(backup);
331                  containerPolicy.hasNext(backupIter);) {
332             Object JavaDoc backupElement = containerPolicy.next(backupIter, getSession());
333
334             // The check for delete depends on the type of merge.
335
if (shouldMergeWorkingCopyIntoOriginal()) {// Source and backup are the same space.
336
if (!sourceSet.containsKey(backupElement)) {
337                     changeOccured = true;
338                     containerPolicy.removeFrom((Object JavaDoc)null, getTargetVersionOfSourceObject(backupElement), target, getSession());
339
340                     // Registered new object in nested units of work must not be registered into the parent,
341
// so this records them in the merge to parent case.
342
if (mapping.isPrivateOwned()) {
343                         registerRemovedNewObjectIfRequired(backupElement);
344                     }
345                 }
346             } else {// Target and backup are same for all types of merge.
347
if (!targetToSources.containsKey(backupElement)) {// If no source for target then was removed.
348
changeOccured = true;
349                     containerPolicy.removeFrom((Object JavaDoc)null, backupElement, target, getSession());// Backup value is same as target value.
350
}
351             }
352         }
353
354         // Handle added elements.
355
for (Object JavaDoc sourceIter = containerPolicy.iteratorFor(source);
356                  containerPolicy.hasNext(sourceIter);) {
357             Object JavaDoc sourceElement = containerPolicy.next(sourceIter, getSession());
358
359             // The target object must be completely merged before adding to the collection
360
// otherwise another thread could pick up the partial object.
361
mapping.cascadeMerge(sourceElement, this);
362             // The check for add depends on the type of merge.
363
if (shouldMergeWorkingCopyIntoOriginal()) {// Source and backup are the same space.
364
if (!backupSet.containsKey(sourceElement)) {
365                     changeOccured = true;
366                     containerPolicy.addInto(getTargetVersionOfSourceObject(sourceElement), target, getSession());
367                 } else {
368                     containerPolicy.validateElementAndRehashIfRequired(sourceElement, target, getSession(), getTargetVersionOfSourceObject(sourceElement));
369                 }
370             } else {// Target and backup are same for all types of merge.
371
Object JavaDoc targetVersionOfSourceElement = getTargetVersionOfSourceObject(sourceElement);
372                 if (!backupSet.containsKey(targetVersionOfSourceElement)) {// Backup value is same as target value.
373
changeOccured = true;
374                     containerPolicy.addInto(targetVersionOfSourceElement, target, getSession());
375                 }
376             }
377         }
378
379         return changeOccured;
380     }
381
382     /**
383      * Recursively merge to rmi clone into the unit of work working copy.
384      * The hastable is used to resolv recursion.
385      */

386     protected Object JavaDoc mergeChangesOfCloneIntoWorkingCopy(Object JavaDoc rmiClone) {
387         ClassDescriptor descriptor = getSession().getDescriptor(rmiClone);
388         Object JavaDoc registeredObject = registerObjectForMergeCloneIntoWorkingCopy(rmiClone);
389
390         if (registeredObject == rmiClone) {
391             //need to find better better fix. prevents merging into itself.
392
return rmiClone;
393         }
394
395         boolean changeTracked = false;
396         try {
397             ObjectBuilder builder = descriptor.getObjectBuilder();
398             
399             if (descriptor.usesVersionLocking()) {
400                 VersionLockingPolicy policy = (VersionLockingPolicy) descriptor.getOptimisticLockingPolicy();
401                 if (policy.isStoredInObject()) {
402                     Object JavaDoc currentValue = builder.extractValueFromObjectForField(registeredObject, policy.getWriteLockField(), session);
403                 
404                     if (policy.isNewerVersion(currentValue, rmiClone, session.keyFromObject(rmiClone), session)) {
405                         throw OptimisticLockException.objectChangedSinceLastMerge(rmiClone);
406                     }
407                 }
408             }
409             
410             // Toggle change tracking during the merge.
411
descriptor.getObjectChangePolicy().dissableEventProcessing(registeredObject);
412             
413             // Merge into the clone from the original, use clone as backup as anything different should be merged.
414
builder.mergeIntoObject(registeredObject, false, rmiClone, this);
415         } finally {
416             descriptor.getObjectChangePolicy().enableEventProcessing(registeredObject);
417         }
418
419         return registeredObject;
420     }
421
422     /**
423      * Recursively merge to original from its parent into the clone.
424      * The hastable is used to resolv recursion.
425      */

426     protected Object JavaDoc mergeChangesOfOriginalIntoWorkingCopy(Object JavaDoc clone) {
427         ClassDescriptor descriptor = getSession().getDescriptor(clone);
428
429         // Find the original object, if it is not there then do nothing.
430
Object JavaDoc original = ((UnitOfWorkImpl)getSession()).getOriginalVersionOfObjectOrNull(clone);
431
432         if (original == null) {
433             return clone;
434         }
435
436         // Merge into the clone from the original, use clone as backup as anything different should be merged.
437
descriptor.getObjectBuilder().mergeIntoObject(clone, false, original, this);
438
439         //update the change policies with the refresh
440
descriptor.getObjectChangePolicy().revertChanges(clone, descriptor, (UnitOfWorkImpl)this.getSession(), ((UnitOfWorkImpl)this.getSession()).getCloneMapping());
441         Vector primaryKey = getSession().keyFromObject(clone);
442         if (descriptor.usesOptimisticLocking()) {
443             descriptor.getOptimisticLockingPolicy().mergeIntoParentCache((UnitOfWorkImpl)getSession(), primaryKey, clone);
444         }
445
446         CacheKey parentCacheKey = ((UnitOfWorkImpl)getSession()).getParent().getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, clone.getClass(), descriptor);
447         CacheKey uowCacheKey = getSession().getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, clone.getClass(), descriptor);
448
449         // Check for null because when there is NoIdentityMap, CacheKey will be null
450
if ((parentCacheKey != null) && (uowCacheKey != null)) {
451             uowCacheKey.setReadTime(parentCacheKey.getReadTime());
452         }
453
454         return clone;
455     }
456
457     /**
458      * Recursively merge to clone into the orignal in its parent.
459      * The hastable is used to resolv recursion.
460      */

461     protected Object JavaDoc mergeChangesOfWorkingCopyIntoOriginal(Object JavaDoc clone, ObjectChangeSet objectChangeSet) {
462         UnitOfWorkImpl unitOfWork = (UnitOfWorkImpl)getSession();
463
464         // This always finds an original different from the clone, even if it has to create one.
465
// This must be done after special cases have been computed because it registers unregistered new objects.
466
Object JavaDoc original = unitOfWork.getOriginalVersionOfObjectOrNull(clone);
467
468         ClassDescriptor descriptor = unitOfWork.getDescriptor(clone.getClass());
469
470         // Always merge into the original.
471
try {
472             if (original == null) {
473                 // if original does not exist then we must merge the entire object
474
original = unitOfWork.buildOriginal(clone);
475                 if (objectChangeSet == null) {
476                     descriptor.getObjectBuilder().mergeIntoObject(original, true, clone, this);
477                 } else if (!objectChangeSet.isNew()) {
478                     //once the original is created we must put it in the cache and
479
//lock it to prevent a reading thread from creating it as well
480
//there will be no deadlock situation because no other threads
481
//will be able to reference this object.
482
AbstractSession parent = unitOfWork.getParent();
483                     original = parent.getIdentityMapAccessorInstance().getWriteLockManager().appendLock(objectChangeSet.getPrimaryKeys(), original, descriptor, this, parent);
484                     descriptor.getObjectBuilder().mergeIntoObject(original, true, clone, this);
485                 } else {
486                     descriptor.getObjectBuilder().mergeChangesIntoObject(original, objectChangeSet, clone, this);
487                 }
488             } else if (objectChangeSet == null) {
489                 // if we have no change set then we must merge the entire object
490
descriptor.getObjectBuilder().mergeIntoObject(original, false, clone, this);
491             } else {
492                 // not null and we have a valid changeSet then merge the changes
493
if (!objectChangeSet.isNew()) {
494                     AbstractSession parent = unitOfWork.getParent();
495                     if(objectChangeSet.shouldInvalidateObject(original, parent)) {
496                         parent.getIdentityMapAccessor().invalidateObject(original);
497                         return clone;
498                     }
499                 }
500                 descriptor.getObjectBuilder().mergeChangesIntoObject(original, objectChangeSet, clone, this);
501             }
502         } catch (QueryException exception) {
503             // Ignore validation errors if unit of work validation is suppressed.
504
// Also there is a very specific case under EJB wrappering where
505
// a related object may have never been accessed in the unit of work context
506
// but is still valid, so this error must be ignored.
507
if (unitOfWork.shouldPerformNoValidation() || (descriptor.hasWrapperPolicy())) {
508                 if ((exception.getErrorCode() != QueryException.BACKUP_CLONE_DELETED) && (exception.getErrorCode() != QueryException.BACKUP_CLONE_IS_ORIGINAL_FROM_PARENT) && (exception.getErrorCode() != QueryException.BACKUP_CLONE_IS_ORIGINAL_FROM_SELF)) {
509                     throw exception;
510                 }
511                 return clone;
512             } else {
513                 throw exception;
514             }
515         }
516
517         if (!unitOfWork.isNestedUnitOfWork()) {
518             Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(clone, unitOfWork);
519
520             // Must ensure the get and put of the cache occur as a single operation.
521
// Cache key hold a reference to a concurrency manager which is used for the lock/release operation
522
CacheKey cacheKey = unitOfWork.getParent().getIdentityMapAccessorInstance().acquireLock(primaryKey, original.getClass(), descriptor);
523             try {
524                 if (descriptor.usesOptimisticLocking()) {
525                     if (descriptor.getOptimisticLockingPolicy().isChildWriteLockValueGreater(unitOfWork, primaryKey, original.getClass())) {
526                         cacheKey.setWriteLockValue(unitOfWork.getIdentityMapAccessor().getWriteLockValue(original));
527                     }
528                 }
529
530                 // Always put in the parent im for root because it must now be persistent.
531
cacheKey.setObject(original);
532                 if (descriptor.getCacheInvalidationPolicy().shouldUpdateReadTimeOnUpdate() || ((objectChangeSet != null) && objectChangeSet.isNew())) {
533                     cacheKey.setReadTime(getSystemTime());
534                 }
535             } finally {
536                 cacheKey.updateAccess();
537                 cacheKey.release();
538             }
539         }
540         return clone;
541     }
542
543     /**
544      * This can be used by the user for merging clones from RMI into the unit of work.
545      */

546     public void mergeCloneIntoWorkingCopy() {
547         setMergePolicy(CLONE_INTO_WORKING_COPY);
548     }
549
550     /**
551      * This is used during the merge of dependent objects referencing independent objects, where you want
552      * the independent objects merged as well.
553      */

554     public void mergeCloneWithReferencesIntoWorkingCopy() {
555         setMergePolicy(CLONE_WITH_REFS_INTO_WORKING_COPY);
556     }
557
558     /**
559      * This is used during cache synchronisation to merge the changes into the distributed cache.
560      */

561     public void mergeIntoDistributedCache() {
562         setMergePolicy(CHANGES_INTO_DISTRIBUTED_CACHE);
563     }
564
565     /**
566      * Merge a change set for a new object into the cache. This method will create a
567      * shell for the new object and then merge the changes from the change set into the object.
568      * The newly merged object will then be added to the cache.
569      */

570     public Object JavaDoc mergeNewObjectIntoCache(ObjectChangeSet changeSet) {
571         if (changeSet.isNew()) {
572             Class JavaDoc objectClass = changeSet.getClassType(session);
573             ClassDescriptor descriptor = getSession().getDescriptor(objectClass);
574             //Try to find the object first we may have merged it all ready
575
Object JavaDoc object = changeSet.getTargetVersionOfSourceObject(getSession(), false);
576             if (object == null) {
577                 if (!getObjectsAlreadyMerged().containsKey(changeSet)) {
578                     // if we haven't merged this object allready then build a new object
579
// otherwise leave it as null which will stop the recursion
580
object = descriptor.getObjectBuilder().buildNewInstance();
581                     //Store the changeset to prevent us from creating this new object again
582
getObjectsAlreadyMerged().put(changeSet, object);
583                 } else {
584                     //we have all ready created the object, must be in a cyclic
585
//merge on a new object so get it out of the allreadymerged collection
586
object = getObjectsAlreadyMerged().get(changeSet);
587                 }
588             } else {
589                 object = changeSet.getTargetVersionOfSourceObject(getSession(), true);
590             }
591             mergeChanges(object, changeSet);
592             Object JavaDoc implementation = descriptor.getObjectBuilder().unwrapObject(object, getSession());
593
594             return getSession().getIdentityMapAccessorInstance().putInIdentityMap(implementation, descriptor.getObjectBuilder().extractPrimaryKeyFromObject(implementation, getSession()), changeSet.getWriteLockValue(), getSystemTime(), descriptor);
595         }
596         return null;
597     }
598
599     /**
600      * This is used to revert changes to objects, or during refreshes.
601      */

602     public void mergeOriginalIntoWorkingCopy() {
603         setMergePolicy(ORIGINAL_INTO_WORKING_COPY);
604     }
605
606     /**
607      * This is used during the unit of work commit to merge changes into the parent.
608      */

609     public void mergeWorkingCopyIntoBackup() {
610         setMergePolicy(WORKING_COPY_INTO_BACKUP);
611     }
612
613     /**
614      * This is used during the unit of work commit to merge changes into the parent.
615      */

616     public void mergeWorkingCopyIntoOriginal() {
617         setMergePolicy(WORKING_COPY_INTO_ORIGINAL);
618     }
619
620     /**
621      * This is used during the unit of work commit to merge changes into the parent.
622      */

623     public void mergeWorkingCopyIntoRemote() {
624         setMergePolicy(WORKING_COPY_INTO_REMOTE);
625     }
626
627     /**
628      * INTERNAL:
629      * This is used to refresh remote session object
630      */

631     public void refreshRemoteObject() {
632         setMergePolicy(REFRESH_REMOTE_OBJECT);
633     }
634
635     /**
636      * INTERNAL:
637      * When merging froma clone when the cache cannot be gaurenteed the object must be first read if it is existing
638      * and not in the cache. Otherwise no changes will be detected as the original state is missing.
639      */

640     protected Object JavaDoc registerObjectForMergeCloneIntoWorkingCopy(Object JavaDoc clone) {
641         ClassDescriptor descriptor = getSession().getDescriptor(clone.getClass());
642         Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(clone, getSession());
643
644         //Must use the java class as this may be a bean that we are merging and it may not have the same class as the
645
// objects in the cache. As of EJB 2.0
646
Object JavaDoc objectFromCache = getSession().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, descriptor.getJavaClass(), descriptor, null);
647         if (objectFromCache != null) {
648             return objectFromCache;
649         }
650
651         oracle.toplink.essentials.queryframework.DoesExistQuery existQuery = descriptor.getQueryManager().getDoesExistQuery();
652
653         // Optimize cache option to avoid executing the does exist query.
654
if (existQuery.shouldCheckCacheForDoesExist()) {
655             return ((UnitOfWorkImpl)getSession()).internalRegisterObject(clone, descriptor);
656         }
657
658         // Check early return to check if it is a new object, i.e. null primary key.
659
Boolean JavaDoc doesExist = (Boolean JavaDoc)existQuery.checkEarlyReturn(clone, primaryKey, getSession(), null);
660         if (doesExist == Boolean.FALSE) {
661             return ((UnitOfWorkImpl)getSession()).internalRegisterObject(clone, descriptor);
662         }
663
664         // Otherwise it is existing and not in the cache so it must be read.
665
Object JavaDoc object = getSession().readObject(clone);
666         if (object == null) {
667             return ((UnitOfWorkImpl)getSession()).internalRegisterObject(clone, descriptor);
668         } else {
669             return object;
670         }
671     }
672
673     /**
674      * Determine if the object is a registered new object, and that this is a nested unit of work
675      * merge into the parent. In this case private mappings will register the object as being removed.
676      */

677     public void registerRemovedNewObjectIfRequired(Object JavaDoc removedObject) {
678         if (getSession().isUnitOfWork()) {
679             UnitOfWorkImpl unitOfWork = (UnitOfWorkImpl)getSession();
680
681             if (shouldMergeWorkingCopyIntoOriginal() && unitOfWork.getParent().isUnitOfWork() && unitOfWork.isCloneNewObject(removedObject)) {
682                 Object JavaDoc originalVersionOfRemovedObject = unitOfWork.getOriginalVersionOfObject(removedObject);
683                 unitOfWork.addRemovedObject(originalVersionOfRemovedObject);
684             }
685         }
686     }
687
688     public void setCascadePolicy(int cascadePolicy) {
689         this.cascadePolicy = cascadePolicy;
690     }
691
692     protected void setMergePolicy(int mergePolicy) {
693         this.mergePolicy = mergePolicy;
694     }
695
696     public void setObjectDescriptors(IdentityHashtable objectDescriptors) {
697         this.objectDescriptors = objectDescriptors;
698     }
699
700     protected void setObjectsAlreadyMerged(IdentityHashtable objectsAlreadyMerged) {
701         this.objectsAlreadyMerged = objectsAlreadyMerged;
702     }
703
704     /**
705      * INTENRAL:
706      * Used to set the node that this merge manager is stored in, within the WriteLocksManager write lockers queue
707      */

708     public void setQueueNode(LinkedNode node) {
709         this.queueNode = node;
710     }
711
712     protected void setSession(AbstractSession session) {
713         this.session = session;
714     }
715
716     /**
717      * INTENRAL:
718      * Used to set the object that the merge manager is waiting on, in order to acquire locks
719      * If this value is null then the merge manager is not waiting on any locks.
720      */

721     public void setWriteLockQueued(CacheKey writeLockQueued) {
722         this.writeLockQueued = writeLockQueued;
723     }
724
725     /**
726      * Flag used to determine that the mappings should be checked for
727      * cascade requirements.
728      */

729     public boolean shouldCascadeByMapping() {
730         return getCascadePolicy() == CASCADE_BY_MAPPING;
731     }
732
733     /**
734      * Flag used to determine if all parts should be cascaded
735      */

736     public boolean shouldCascadeAllParts() {
737         return getCascadePolicy() == CASCADE_ALL_PARTS;
738     }
739
740     /**
741      * Flag used to determine if any parts should be cascaded
742      */

743     public boolean shouldCascadeParts() {
744         return getCascadePolicy() != NO_CASCADE;
745     }
746
747     /**
748      * Flag used to determine if any private parts should be cascaded
749      */

750     public boolean shouldCascadePrivateParts() {
751         return (getCascadePolicy() == CASCADE_PRIVATE_PARTS) || (getCascadePolicy() == CASCADE_ALL_PARTS);
752     }
753
754     /**
755      * Refreshes are based on the objects row, so all attributes of the object must be refreshed.
756      * However merging from RMI, normally reference are made transient, so should not be merge unless
757      * specified.
758      */

759     public boolean shouldCascadeReferences() {
760         return !shouldMergeCloneIntoWorkingCopy();
761     }
762
763     /**
764      * INTERNAL:
765      * This happens when changes from an UnitOfWork is propagated to a distributed class.
766      */

767     public boolean shouldMergeChangesIntoDistributedCache() {
768         return getMergePolicy() == CHANGES_INTO_DISTRIBUTED_CACHE;
769     }
770
771     /**
772      * This can be used by the user for merging clones from RMI into the unit of work.
773      */

774     public boolean shouldMergeCloneIntoWorkingCopy() {
775         return getMergePolicy() == CLONE_INTO_WORKING_COPY;
776     }
777
778     /**
779      * This can be used by the user for merging remote EJB objects into the unit of work.
780      */

781     public boolean shouldMergeCloneWithReferencesIntoWorkingCopy() {
782         return getMergePolicy() == CLONE_WITH_REFS_INTO_WORKING_COPY;
783     }
784
785     /**
786      * This is used to revert changes to objects, or during refreshes.
787      */

788     public boolean shouldMergeOriginalIntoWorkingCopy() {
789         return getMergePolicy() == ORIGINAL_INTO_WORKING_COPY;
790     }
791
792     /**
793      * This is used during the unit of work commit to merge changes into the parent.
794      */

795     public boolean shouldMergeWorkingCopyIntoBackup() {
796         return getMergePolicy() == WORKING_COPY_INTO_BACKUP;
797     }
798
799     /**
800      * This is used during the unit of work commit to merge changes into the parent.
801      */

802     public boolean shouldMergeWorkingCopyIntoOriginal() {
803         return getMergePolicy() == WORKING_COPY_INTO_ORIGINAL;
804     }
805
806     /**
807      * INTERNAL:
808      * This happens when serialized remote unit of work has to be merged with local remote unit of work.
809      */

810     public boolean shouldMergeWorkingCopyIntoRemote() {
811         return getMergePolicy() == WORKING_COPY_INTO_REMOTE;
812     }
813
814     /**
815      * INTERNAL:
816      * This is used to refresh objects on the remote session
817      */

818     public boolean shouldRefreshRemoteObject() {
819         return getMergePolicy() == REFRESH_REMOTE_OBJECT;
820     }
821 }
822
Popular Tags