KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.io.*;
26 import javax.persistence.EntityExistsException;
27 import oracle.toplink.essentials.internal.helper.*;
28 import oracle.toplink.essentials.internal.descriptors.*;
29 import oracle.toplink.essentials.internal.localization.ExceptionLocalization;
30 import oracle.toplink.essentials.platform.server.ServerPlatform;
31 import oracle.toplink.essentials.queryframework.*;
32 import oracle.toplink.essentials.internal.identitymaps.*;
33 import oracle.toplink.essentials.internal.databaseaccess.*;
34 import oracle.toplink.essentials.expressions.*;
35 import oracle.toplink.essentials.exceptions.*;
36 import oracle.toplink.essentials.internal.sequencing.Sequencing;
37 import oracle.toplink.essentials.logging.SessionLog;
38 import oracle.toplink.essentials.internal.localization.LoggingLocalization;
39 import oracle.toplink.essentials.sessions.SessionProfiler;
40 import oracle.toplink.essentials.sessions.UnitOfWork;
41 import oracle.toplink.essentials.internal.sessions.AbstractSession;
42 import oracle.toplink.essentials.descriptors.DescriptorEventManager;
43 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
44 import oracle.toplink.essentials.internal.helper.IdentityHashtable;
45
46 import oracle.toplink.essentials.descriptors.ClassDescriptor;
47 import oracle.toplink.essentials.internal.queryframework.JoinedAttributeManager;
48
49 /**
50  * Implementation of oracle.toplink.essentials.sessions.UnitOfWork
51  * The public interface should be used by public API and testing, the implementation should be used internally.
52  * @see oracle.toplink.essentials.sessions.UnitOfWork
53  *
54  * <b>Purpose</b>: To allow object level transactions.
55  * <p>
56  * <b>Description</b>: The unit of work is a session that implements all of the normal
57  * protocol of a TopLink session. It can be spawned from any other session including another unit of work.
58  * Objects can be brought into the unit of work through reading them or through registering them.
59  * The unit of work will opperate on its own object space, that is the objects within the unit of work
60  * will be clones of the orignial objects. When the unit of work is commited, all changes to any objects
61  * registered within the unit of work will be commited to the database. A minimal commit/update will
62  * be performed and any foreign keys/circular reference/referencial integrity will be resolved.
63  * If the commit to the database is successful the changed objects will be merged back into the unit of work
64  * parent session.
65  * <p>
66  * <b>Responsibilities</b>:
67  * <ul>
68  * <li> Allow parallel transactions against a session's objects.
69  * <li> Allow nested transactions.
70  * <li> Not require the application to write objects that is changes, automatically determine what has changed.
71  * <li> Perform a minimal commit/update of all changes that occured.
72  * <li> Resolve foreign keys for newly created objects and maintain referencial integrity.
73  * <li> Allow for the object transaction to use its own object space.
74  * </ul>
75  */

76 public class UnitOfWorkImpl extends AbstractSession implements oracle.toplink.essentials.sessions.UnitOfWork {
77
78     /** Fix made for weak caches to avoid garbage collection of the originals. **/
79     /** As well as used as lookup in merge algorithm for aggregates and others **/
80     protected transient IdentityHashtable cloneToOriginals;
81     protected transient AbstractSession parent;
82
83     /** Hashtable of all the clones. The key contains the clone of the object. */
84     protected IdentityHashtable cloneMapping;
85     protected IdentityHashtable newObjectsCloneToOriginal;
86     protected IdentityHashtable newObjectsOriginalToClone;
87     protected IdentityHashtable deletedObjects;
88
89     /** This member variable contains a copy of all of the clones for this particular UOW */
90     protected IdentityHashtable allClones;
91     protected IdentityHashtable objectsDeletedDuringCommit;
92     protected IdentityHashtable removedObjects;
93     protected IdentityHashtable unregisteredNewObjects;
94     protected IdentityHashtable unregisteredExistingObjects;
95
96     protected IdentityHashtable newAggregates;
97
98     /** This method is used to store the current changeSet for this UnitOfWork. */
99     protected UnitOfWorkChangeSet unitOfWorkChangeSet;
100
101     /** use to track pessimistic locked objects */
102     protected IdentityHashtable pessimisticLockedObjects;
103
104     /** Used to store the list of locks that this UnitOfWork has acquired for this merge */
105     protected MergeManager lastUsedMergeManager;
106
107     /** Read-only class can be used for reference data to avoid cloning when not required. */
108     protected Hashtable readOnlyClasses;
109
110     /** Flag indicating that the transaction for this UOW was already begun. */
111     protected boolean wasTransactionBegunPrematurely;
112
113     /** Allow for double merges of new objects by putting them into the cache. */
114     protected boolean shouldNewObjectsBeCached;
115
116     /** Flag indicating that deletes should be performed before other updates. */
117     protected boolean shouldPerformDeletesFirst;
118
119     /** Flag indicating how to deal with exceptions on conforming queries. **/
120     protected int shouldThrowConformExceptions;
121
122     /** The amount of validation can be configured. */
123     protected int validationLevel;
124     static public final int None = 0;
125     static public final int Partial = 1;
126     static public final int Full = 2;
127
128     /**
129      * With the new synchronized unit of work, need a lifecycle state variable to
130      * track birth, commited, pending_merge and death.
131      */

132     protected boolean isSynchronized;
133     protected int lifecycle;
134     public static final int Birth = 0;
135     public static final int CommitPending = 1;
136
137     // After a call to writeChanges() but before commit.
138
public static final int CommitTransactionPending = 2;
139
140     // After an unsuccessful call to writeChanges(). No recovery at all.
141
public static final int WriteChangesFailed = 3;
142     public static final int MergePending = 4;
143     public static final int Death = 5;
144     public static final int AfterExternalTransactionRolledBack = 6;
145
146     /** Used for Conforming Queries */
147     public static final int DO_NOT_THROW_CONFORM_EXCEPTIONS = 0;
148     public static final int THROW_ALL_CONFORM_EXCEPTIONS = 1;
149     
150     public static final String JavaDoc LOCK_QUERIES_PROPERTY = "LockQueriesProperties";
151
152     /** Used for merging dependent values without use of WL SessionAccessor */
153     protected static boolean SmartMerge = false;
154
155     /** Kept reference of read lock objects*/
156     protected Hashtable optimisticReadLockObjects;
157
158     /** lazy initialization done in storeModifyAllQuery. For UpdateAllQuery, only clones of all UpdateAllQuery's (deferred and non-deferred) are stored here for validation only.*/
159     protected List modifyAllQueries;
160
161     /** Contains deferred ModifyAllQuery's that have translation row for execution only. At commit their clones will be added to modifyAllQueries for validation afterwards*/
162      //Bug4607551
163
protected List deferredModifyAllQueries;
164
165     /**
166      * Used during the cloning process to track the recursive depth in. This will
167      * be used to determine at which point the process can begin to wait on locks
168      * without being concerned about creating deadlock situations.
169      */

170     protected int cloneDepth = 0;
171
172     /**
173      * This collection will be used to store those objects that are currently locked
174      * for the clone process. It should be populated with an TopLinkIdentityHashMap
175      */

176     protected Map objectsLockedForClone;
177         
178     /**
179      * PERF: Stores the JTA transaction to optimize activeUnitOfWork lookup.
180      */

181     protected Object JavaDoc transaction;
182     
183
184
185
186
187     
188     /**
189      * PERF: Cache the write-lock check to avoid cost of checking in every register/clone.
190      */

191     protected boolean shouldCheckWriteLock;
192
193     /**
194      * True if UnitOfWork should be resumed on completion of transaction.
195      * Used when UnitOfWork is Synchronized with external transaction control
196      */

197     protected boolean resumeOnTransactionCompletion;
198     
199     /**
200      * True if either DataModifyQuery or ModifyAllQuery was executed.
201      * Gets reset on commit, effects DoesExistQuery behaviour and reading.
202      */

203     protected boolean wasNonObjectLevelModifyQueryExecuted;
204
205     /**
206      * True if the value holder for the joined attribute should be triggered.
207      * Required by ejb30 fetch join.
208      */

209     protected boolean shouldCascadeCloneToJoinedRelationship;
210
211     /**
212      * INTERNAL:
213      * Create and return a new unit of work with the sesson as its parent.
214      */

215     public UnitOfWorkImpl(AbstractSession parent) {
216         super();
217         this.name = parent.getName();
218         this.parent = parent;
219         // 2612538 - the default size of IdentityHashtable (32) is appropriate
220
this.cloneMapping = new IdentityHashtable();
221         // PERF: lazy-init hashtables (3286089) - cloneToOriginals,
222
// newObjectsInParentOriginalToClone, objectsDeletedDuringCommit
223
// removedObjects.
224
this.project = parent.getProject();
225         this.profiler = parent.getProfiler();
226         this.isInProfile = parent.isInProfile;
227         this.sessionLog = parent.getSessionLog();
228         this.eventManager = parent.getEventManager().clone(this);
229         this.exceptionHandler = parent.getExceptionHandler();
230
231         // Initialize the readOnlyClasses variable.
232
this.setReadOnlyClasses(parent.copyReadOnlyClasses());
233         this.wasTransactionBegunPrematurely = false;
234         // False by default as this may screw up things for objects with 0, -1 or other non-null default keys.
235
this.shouldNewObjectsBeCached = false;
236         this.validationLevel = Partial;
237
238         this.shouldPerformDeletesFirst = false;
239
240         // for 3.0.x this conforming queries will not throw exceptions unless explicitly asked to
241
this.shouldThrowConformExceptions = DO_NOT_THROW_CONFORM_EXCEPTIONS;
242
243         // initialize lifecycle state variable
244
this.isSynchronized = false;
245         this.lifecycle = Birth;
246         // PERF: Cache the write-lock check to avoid cost of checking in every register/clone.
247
this.shouldCheckWriteLock = parent.getDatasourceLogin().shouldSynchronizedReadOnWrite() || parent.getDatasourceLogin().shouldSynchronizeWrites();
248         this.resumeOnTransactionCompletion = false;
249
250         getEventManager().postAcquireUnitOfWork();
251         incrementProfile(SessionProfiler.UowCreated);
252     }
253
254     /**
255      * PUBLIC:
256      * Nested units of work are not supported in TopLink Essentials.
257      */

258     public UnitOfWork acquireUnitOfWork() {
259         throw ValidationException.notSupported("acquireUnitOfWork", getClass());
260     }
261
262     /**
263      * INTERNAL:
264      * Register a new aggregate object with the unit of work.
265      */

266     public void addNewAggregate(Object JavaDoc originalObject) {
267         getNewAggregates().put(originalObject, originalObject);
268     }
269
270     /**
271      * INTERNAL:
272      * Add object deleted during root commit of unit of work.
273      */

274     public void addObjectDeletedDuringCommit(Object JavaDoc object, ClassDescriptor descriptor) {
275         // The object's key is keyed on the object, this avoids having to compute the key later on.
276
getObjectsDeletedDuringCommit().put(object, keyFromObject(object, descriptor));
277         //bug 4730595: changed to add deleted objects to the changesets.
278
((UnitOfWorkChangeSet)getUnitOfWorkChangeSet()).addDeletedObject(object, this);
279     }
280
281     /**
282      * PUBLIC:
283      * Adds the given Java class to the receiver's set of read-only classes.
284      * Cannot be called after objects have been registered in the unit of work.
285      */

286     public void addReadOnlyClass(Class JavaDoc theClass) throws ValidationException {
287         if (!canChangeReadOnlySet()) {
288             throw ValidationException.cannotModifyReadOnlyClassesSetAfterUsingUnitOfWork();
289         }
290
291         getReadOnlyClasses().put(theClass, theClass);
292
293         ClassDescriptor descriptor = getDescriptor(theClass);
294
295         // Also mark all subclasses as read-only.
296
if (descriptor.hasInheritance()) {
297             for (Enumeration childEnum = descriptor.getInheritancePolicy().getChildDescriptors().elements();
298                      childEnum.hasMoreElements();) {
299                 ClassDescriptor childDescriptor = (ClassDescriptor)childEnum.nextElement();
300                 addReadOnlyClass(childDescriptor.getJavaClass());
301             }
302         }
303     }
304
305     /**
306      * PUBLIC:
307      * Adds the classes in the given Vector to the existing set of read-only classes.
308      * Cannot be called after objects have been registered in the unit of work.
309      */

310     public void addReadOnlyClasses(Vector classes) {
311         for (Enumeration enumtr = classes.elements(); enumtr.hasMoreElements();) {
312             Class JavaDoc theClass = (Class JavaDoc)enumtr.nextElement();
313             addReadOnlyClass(theClass);
314         }
315     }
316
317     /**
318      * INTERNAL:
319      * Register that an object was removed in a nested unit of work.
320      */

321     public void addRemovedObject(Object JavaDoc orignal) {
322         getRemovedObjects().put(orignal, orignal);// Use as set.
323
}
324
325     /**
326      * ADVANCED:
327      * Assign sequence number to the object.
328      * This allows for an object's id to be assigned before commit.
329      * It can be used if the application requires to use the object id before the object exists on the database.
330      * Normally all ids are assigned during the commit automatically.
331      */

332     public void assignSequenceNumber(Object JavaDoc object) throws DatabaseException {
333         //** sequencing refactoring
334
startOperationProfile(SessionProfiler.AssignSequence);
335         try {
336             ObjectBuilder builder = getDescriptor(object).getObjectBuilder();
337
338             // This is done outside of a transaction to ensure optimial concurrency and deadlock avoidance in the sequence table.
339
if (builder.getDescriptor().usesSequenceNumbers() && !getSequencing().shouldAcquireValueAfterInsert(object.getClass())) {
340                 Object JavaDoc implementation = builder.unwrapObject(object, this);
341                 builder.assignSequenceNumber(implementation, this);
342             }
343         } catch (RuntimeException JavaDoc exception) {
344             handleException(exception);
345         }
346         endOperationProfile(SessionProfiler.AssignSequence);
347     }
348
349     /**
350      * ADVANCED:
351      * Assign sequence numbers to all new objects registered in this unit of work,
352      * or any new objects reference by any objects registered.
353      * This allows for an object's id to be assigned before commit.
354      * It can be used if the application requires to use the object id before the object exists on the database.
355      * Normally all ids are assigned during the commit automatically.
356      */

357     public void assignSequenceNumbers() throws DatabaseException {
358         // This should be done outside of a transaction to ensure optimal concurrency and deadlock avoidance in the sequence table.
359
// discoverAllUnregisteredNewObjects() should be called no matter whether sequencing used
360
// or not, because collectAndPrepareObjectsForCommit() method (which calls assignSequenceNumbers())
361
// needs it.
362
// It would be logical to remove discoverAllUnregisteredNewObjects() from assignSequenceNumbers()
363
// and make collectAndPrepareObjectsForCommit() to call discoverAllUnregisteredNewObjects()
364
// first and assignSequenceNumbers() next,
365
// but assignSequenceNumbers() is a public method which could be called by user - and
366
// in this case discoverAllUnregisteredNewObjects() is needed again (though
367
// if sequencing is not used the call will make no sense - but no harm, too).
368
discoverAllUnregisteredNewObjects();
369         Sequencing sequencing = getSequencing();
370         if (sequencing == null) {
371             return;
372         }
373         int whenShouldAcquireValueForAll = sequencing.whenShouldAcquireValueForAll();
374         if (whenShouldAcquireValueForAll == Sequencing.AFTER_INSERT) {
375             return;
376         }
377         boolean shouldAcquireValueBeforeInsertForAll = whenShouldAcquireValueForAll == Sequencing.BEFORE_INSERT;
378         startOperationProfile(SessionProfiler.AssignSequence);
379         Enumeration unregisteredNewObjectsEnum = getUnregisteredNewObjects().keys();
380         while (unregisteredNewObjectsEnum.hasMoreElements()) {
381             Object JavaDoc object = unregisteredNewObjectsEnum.nextElement();
382             if (getDescriptor(object).usesSequenceNumbers() && ((!isObjectRegistered(object)) || isCloneNewObject(object)) && (shouldAcquireValueBeforeInsertForAll || !sequencing.shouldAcquireValueAfterInsert(object.getClass()))) {
383                 getDescriptor(object).getObjectBuilder().assignSequenceNumber(object, this);
384             }
385         }
386         Enumeration registeredNewObjectsEnum = getNewObjectsCloneToOriginal().keys();
387         while (registeredNewObjectsEnum.hasMoreElements()) {
388             Object JavaDoc object = registeredNewObjectsEnum.nextElement();
389             if (getDescriptor(object).usesSequenceNumbers() && ((!isObjectRegistered(object)) || isCloneNewObject(object)) && (shouldAcquireValueBeforeInsertForAll || !sequencing.shouldAcquireValueAfterInsert(object.getClass()))) {
390                 getDescriptor(object).getObjectBuilder().assignSequenceNumber(object, this);
391             }
392         }
393
394         endOperationProfile(SessionProfiler.AssignSequence);
395     }
396
397     /**
398      * PUBLIC:
399      * Tell the unit of work to begin a transaction now.
400      * By default the unit of work will begin a transaction at commit time.
401      * The default is the recommended approach, however sometimes it is
402      * neccessary to start the transaction before commit time. When the
403      * unit of work commits, this transcation will be commited.
404      *
405      * @see #commit()
406      * @see #release()
407      */

408     public void beginEarlyTransaction() throws DatabaseException {
409         beginTransaction();
410         setWasTransactionBegunPrematurely(true);
411     }
412
413     /**
414      * INTERNAL:
415      * This is internal to the uow, transactions should not be used explictly in a uow.
416      * The uow shares its parents transactions.
417      */

418     public void beginTransaction() throws DatabaseException {
419         getParent().beginTransaction();
420     }
421
422     /**
423      * INTERNAL:
424      * Unregistered new objects have no original so we must create one for commit and resume and
425      * to put into the parent. We can NEVER let the same copy of an object exist in multiple units of work.
426      */

427     public Object JavaDoc buildOriginal(Object JavaDoc workingClone) {
428         ClassDescriptor descriptor = getDescriptor(workingClone);
429         ObjectBuilder builder = descriptor.getObjectBuilder();
430         Object JavaDoc original = builder.instantiateClone(workingClone, this);
431
432         // If no original exists can mean any of the following:
433
// -A RemoteUnitOfWork and cloneToOriginals is transient.
434
// -A clone read while in transaction, and built directly from
435
// the database row with no intermediary original.
436
// -An unregistered new object
437
if (checkIfAlreadyRegistered(workingClone, descriptor) != null) {
438             getCloneToOriginals().put(workingClone, original);
439             return original;
440         } else {
441             // Assume it is an unregisteredNewObject, but this is worrisome, as
442
// it may be an unregistered existing object, not in the parent cache?
443
Object JavaDoc backup = builder.instantiateClone(workingClone, this);
444             
445             // Original is fine for backup as state is the same.
446
getCloneMapping().put(workingClone, backup);
447
448             // Must register new instance / clone as the original.
449
getNewObjectsCloneToOriginal().put(workingClone, original);
450             getNewObjectsOriginalToClone().put(original, workingClone);
451
452             // no need to register in identity map as the DatabaseQueryMechanism will have
453
//placed the object in the identity map on insert. bug 3431586
454
}
455         return original;
456     }
457
458     /**
459      * INTERNAL:
460      *<p> This Method is designed to calculate the changes for all objects
461      * within the PendingObjects.
462      */

463     public UnitOfWorkChangeSet calculateChanges(IdentityHashtable allObjects, UnitOfWorkChangeSet changeSet) {
464         getEventManager().preCalculateUnitOfWorkChangeSet();
465
466         Enumeration objects = allObjects.elements();
467         while (objects.hasMoreElements()) {
468             Object JavaDoc object = objects.nextElement();
469
470             //block of code removed because it will never be touched see bug # 2903565
471
ClassDescriptor descriptor = getDescriptor(object);
472
473             //Block of code removed for code coverage, as it would never have been touched. bug # 2903600
474
// Use the object change policy to determine if we should run a comparison for this object - TGW
475
if (descriptor.getObjectChangePolicy().shouldCompareForChange(object, this, descriptor)) {
476                 ObjectChangeSet changes = descriptor.getObjectChangePolicy().calculateChanges(object, getBackupClone(object), changeSet, this, descriptor, true);
477                 if ((changes != null) && changes.isNew()) {
478                     // add it to the new list as well so we do not loose it as it may not have a valid primary key
479
// it will be moved to the standard list once it is inserted.
480
changeSet.addNewObjectChangeSet(changes, this);
481                 } else {
482                     changeSet.addObjectChangeSet(changes);
483                 }
484             }
485         }
486         
487         getEventManager().postCalculateUnitOfWorkChangeSet(changeSet);
488         return changeSet;
489     }
490
491     /**
492      * INTERNAL:
493      * Checks whether the receiver has been used. i.e. objects have been registered.
494      *
495      * @return true or false depending on whether the read-only set can be changed or not.
496      */

497     protected boolean canChangeReadOnlySet() {
498         return !hasCloneMapping() && !hasDeletedObjects();
499     }
500
501     /**
502      * INTERNAL:
503      */

504     public boolean checkForUnregisteredExistingObject(Object JavaDoc object) {
505         ClassDescriptor descriptor = getDescriptor(object.getClass());
506         Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(object, this);
507
508         DoesExistQuery existQuery = descriptor.getQueryManager().getDoesExistQuery();
509
510         existQuery = (DoesExistQuery)existQuery.clone();
511         existQuery.setObject(object);
512         existQuery.setPrimaryKey(primaryKey);
513         existQuery.setDescriptor(descriptor);
514         existQuery.setCheckCacheFirst(true);
515
516         if (((Boolean JavaDoc)executeQuery(existQuery)).booleanValue()) {
517             return true;
518         } else {
519             return false;
520         }
521     }
522
523     /**
524      * INTERNAL:
525      * Register the object and return the clone if it is existing otherwise return null if it is new.
526      * The unit of work determines existence during registration, not during the commit.
527      */

528     public Object JavaDoc checkExistence(Object JavaDoc object) {
529         ClassDescriptor descriptor = getDescriptor(object.getClass());
530         Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(object, this);
531         // PERF: null primary key cannot exist.
532
if (primaryKey.contains(null)) {
533             return null;
534         }
535         DoesExistQuery existQuery = descriptor.getQueryManager().getDoesExistQuery();
536
537         existQuery = (DoesExistQuery)existQuery.clone();
538         existQuery.setObject(object);
539         existQuery.setPrimaryKey(primaryKey);
540         existQuery.setDescriptor(descriptor);
541         existQuery.setCheckCacheFirst(true);
542
543         if (((Boolean JavaDoc)executeQuery(existQuery)).booleanValue()) {
544             //we know if it exists or not, now find or register it
545
Object JavaDoc objectFromCache = getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, object.getClass(), descriptor, null);
546     
547             if (objectFromCache != null) {
548                 // Ensure that the registered object is the one from the parent cache.
549
if (shouldPerformFullValidation()) {
550                     if ((objectFromCache != object) && (getParent().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, object.getClass(), descriptor, null) != object)) {
551                         throw ValidationException.wrongObjectRegistered(object, objectFromCache);
552                     }
553                 }
554     
555                 // Has already been cloned.
556
if (!this.isObjectDeleted(objectFromCache))
557                     return objectFromCache;
558             }
559             // This is a case where the object is not in the session cache,
560
// so a new cache-key is used as there is no original to use for locking.
561
return cloneAndRegisterObject(object, new CacheKey(primaryKey), null);
562         } else {
563             return null;
564         }
565     }
566
567     /**
568      * INTERNAL:
569      * Return the value of the object if it already is registered, otherwise null.
570      */

571     protected Object JavaDoc checkIfAlreadyRegistered(Object JavaDoc object, ClassDescriptor descriptor) {
572         // Don't register read-only classes
573
if (isClassReadOnly(object.getClass(), descriptor)) {
574             return null;
575         }
576
577         // Check if the working copy is again being registered in which case we return the same working copy
578
Object JavaDoc registeredObject = getCloneMapping().get(object);
579         if (registeredObject != null) {
580             return object;
581         }
582
583         // Check if object exists in my new objects if it is in the new objects cache then it means domain object is being
584
// re-registered and we should return the same working clone. This check holds only for the new registered objects
585
// PERF: Avoid initialization of new objects if none.
586
if (hasNewObjects()) {
587             registeredObject = getNewObjectsOriginalToClone().get(object);
588             if (registeredObject != null) {
589                 return registeredObject;
590             }
591         }
592
593         return null;
594     }
595
596     /**
597      * ADVANCED:
598      * Register the new object with the unit of work.
599      * This will register the new object with cloning.
600      * Normally the registerObject method should be used for all registration of new and existing objects.
601      * This version of the register method can only be used for new objects.
602      * This method should only be used if a new object is desired to be registered without an existence Check.
603      *
604      * @see #registerObject(Object)
605      */

606     protected Object JavaDoc cloneAndRegisterNewObject(Object JavaDoc original) {
607         ClassDescriptor descriptor = getDescriptor(original);
608         ObjectBuilder builder = descriptor.getObjectBuilder();
609
610         // bug 2612602 create the working copy object.
611
Object JavaDoc clone = builder.instantiateWorkingCopyClone(original, this);
612
613         // Must put in the original to clone to resolv circular refs.
614
getNewObjectsOriginalToClone().put(original, clone);
615         // Must put in clone mapping.
616
getCloneMapping().put(clone, clone);
617
618         builder.populateAttributesForClone(original, clone, this, null);
619
620         // Must reregister in both new objects.
621
registerNewObjectClone(clone, original);
622
623         //Build backup clone for DeferredChangeDetectionPolicy or ObjectChangeTrackingPolicy,
624
//but not for AttributeChangeTrackingPolicy
625
Object JavaDoc backupClone = descriptor.getObjectChangePolicy().buildBackupClone(clone, builder, this);
626         getCloneMapping().put(clone, backupClone);// The backup clone must be updated.
627

628         return clone;
629     }
630
631     /**
632      * INTERNAL:
633      * Clone and register the object.
634      * The cache key must the cache key from the session cache,
635      * as it will be used for locking.
636      */

637     public Object JavaDoc cloneAndRegisterObject(Object JavaDoc original, CacheKey cacheKey, JoinedAttributeManager joinedAttributeManager) {
638         ClassDescriptor descriptor = getDescriptor(original);
639
640         ObjectBuilder builder = descriptor.getObjectBuilder();
641         Object JavaDoc workingClone = builder.instantiateWorkingCopyClone(original, this);
642
643         // The cache/objects being registered must first be locked to ensure
644
// that a merge or refresh does not oocur on the object while being cloned to
645
// avoid cloning a partially merged/refreshed object.
646
// If a cache isolation level is used, then lock the entire cache.
647
// otherwise lock the object and it related objects (not using indirection) as a unit.
648
// If just a simple object (all indirection) a simple read-lock can be used.
649
// PERF: Cache if check to write is required.
650
boolean identityMapLocked = this.shouldCheckWriteLock && getParent().getIdentityMapAccessorInstance().acquireWriteLock();
651         boolean rootOfCloneRecursion = false;
652         if ((!identityMapLocked) && (this.objectsLockedForClone == null)) {//we may have locked all required objects already
653
// PERF: If a simple object just acquire a simple read-lock.
654
if (descriptor.shouldAcquireCascadedLocks()) {
655                 this.objectsLockedForClone = getParent().getIdentityMapAccessorInstance().getWriteLockManager().acquireLocksForClone(original, descriptor, cacheKey.getKey(), getParent());
656             } else {
657                 cacheKey.acquireReadLock();
658             }
659             rootOfCloneRecursion = true;
660         }
661         try {
662             // This must be registered before it is built to avoid really obscure cycles.
663
getCloneMapping().put(workingClone, workingClone);
664
665             //also clone the fetch group reference if applied
666
if (descriptor.hasFetchGroupManager()) {
667                 descriptor.getFetchGroupManager().copyFetchGroupInto(original, workingClone);
668             }
669
670             //store this for look up later
671
getCloneToOriginals().put(workingClone, original);
672             // just clone it.
673
populateAndRegisterObject(original, workingClone, cacheKey.getKey(), descriptor, cacheKey.getWriteLockValue(), cacheKey.getReadTime(), joinedAttributeManager);
674
675         } finally {
676             // If the entire cache was locke, release the cache lock,
677
// otherwise either release the cache-key for a simple lock,
678
// otherwise release the entire set of locks for related objects if this was the root.
679
if (identityMapLocked) {
680                 getParent().getIdentityMapAccessorInstance().releaseWriteLock();
681             } else {
682                 if (rootOfCloneRecursion) {
683                     if (this.objectsLockedForClone == null) {
684                         cacheKey.releaseReadLock();
685                     } else {
686                         for (Iterator iterator = this.objectsLockedForClone.values().iterator();
687                                  iterator.hasNext();) {
688                             ((CacheKey)iterator.next()).releaseReadLock();
689                         }
690                         this.objectsLockedForClone = null;
691                     }
692                 }
693             }
694         }
695         return workingClone;
696     }
697
698     /**
699      * INTERNAL:
700      * Prepare for commit.
701      */

702     public IdentityHashtable collectAndPrepareObjectsForCommit() {
703         IdentityHashtable changedObjects = new IdentityHashtable(1 + getCloneMapping().size());
704
705         // SPECJ: Avoid for CMP.
706
if (! getProject().isPureCMP2Project()) {
707             assignSequenceNumbers();
708         }
709
710         //assignSequenceNumbers will collect the unregistered new objects and assign id's to all new
711
// objects
712
// Add any registered objects.
713
for (Enumeration clonesEnum = getCloneMapping().keys(); clonesEnum.hasMoreElements();) {
714             Object JavaDoc clone = clonesEnum.nextElement();
715             changedObjects.put(clone, clone);
716         }
717         for (Enumeration unregisteredNewObjectsEnum = getUnregisteredNewObjects().keys();
718                  unregisteredNewObjectsEnum.hasMoreElements();) {
719             Object JavaDoc newObject = unregisteredNewObjectsEnum.nextElement();
720             changedObjects.put(newObject, newObject);
721         }
722
723         return changedObjects;
724     }
725
726     /**
727      * INTERNAL:
728      * Prepare for merge in nested uow.
729      */

730     public IdentityHashtable collectAndPrepareObjectsForNestedMerge() {
731         IdentityHashtable changedObjects = new IdentityHashtable(1 + getCloneMapping().size());
732
733         discoverAllUnregisteredNewObjects();
734
735         //assignSequenceNumbers will collect the unregistered new objects and assign id's to all new
736
// objects
737
// Add any registered objects.
738
for (Enumeration clonesEnum = getCloneMapping().keys(); clonesEnum.hasMoreElements();) {
739             Object JavaDoc clone = clonesEnum.nextElement();
740             changedObjects.put(clone, clone);
741         }
742         for (Enumeration unregisteredNewObjectsEnum = getUnregisteredNewObjects().keys();
743                  unregisteredNewObjectsEnum.hasMoreElements();) {
744             Object JavaDoc newObject = unregisteredNewObjectsEnum.nextElement();
745             changedObjects.put(newObject, newObject);
746         }
747
748         return changedObjects;
749     }
750
751     /**
752      * PUBLIC:
753      * Commit the unit of work to its parent.
754      * For a nested unit of work this will merge any changes to its objects
755      * with its parents.
756      * For a first level unit of work it will commit all changes to its objects
757      * to the database as a single transaction. If successful the changes to its
758      * objects will be merged to its parent's objects. If the commit fails the database
759      * transaction will be rolledback, and the unit of work will be released.
760      * If the commit is successful the unit of work is released, and a new unit of work
761      * must be acquired if further changes are desired.
762      *
763      * @see #commitAndResumeOnFailure()
764      * @see #commitAndResume()
765      * @see #release()
766      */

767     public void commit() throws DatabaseException, OptimisticLockException {
768         //CR#2189 throwing exception if UOW try to commit again(XC)
769
if (!isActive()) {
770             throw ValidationException.cannotCommitUOWAgain();
771         }
772         if (isAfterWriteChangesFailed()) {
773             throw ValidationException.unitOfWorkAfterWriteChangesFailed("commit");
774         }
775
776         if (!isNestedUnitOfWork()) {
777             if (isSynchronized()) {
778                 // If we started the JTS transaction then we have to commit it as well.
779
if (getParent().wasJTSTransactionInternallyStarted()) {
780                     commitInternallyStartedExternalTransaction();
781                 }
782
783                 // Do not commit until the JTS wants to.
784
return;
785             }
786         }
787         if (getLifecycle() == CommitTransactionPending) {
788             commitAfterWriteChanges();
789             return;
790         }
791         log(SessionLog.FINER, SessionLog.TRANSACTION, "begin_unit_of_work_commit");// bjv - correct spelling
792
getEventManager().preCommitUnitOfWork();
793         setLifecycle(CommitPending);
794         commitRootUnitOfWork();
795         getEventManager().postCommitUnitOfWork();
796         log(SessionLog.FINER, SessionLog.TRANSACTION, "end_unit_of_work_commit");
797         release();
798     }
799
800     /**
801      * PUBLIC:
802      * Commit the unit of work to its parent.
803      * For a nested unit of work this will merge any changes to its objects
804      * with its parents.
805      * For a first level unit of work it will commit all changes to its objects
806      * to the database as a single transaction. If successful the changes to its
807      * objects will be merged to its parent's objects. If the commit fails the database
808      * transaction will be rolledback, and the unit of work will be released.
809      * The normal commit releases the unit of work, forcing a new one to be acquired if further changes are desired.
810      * The resuming feature allows for the same unit of work (and working copies) to be continued to be used.
811      *
812      * @see #commitAndResumeOnFailure()
813      * @see #commit()
814      * @see #release()
815      */

816     public void commitAndResume() throws DatabaseException, OptimisticLockException {
817         //CR#2189 throwing exception if UOW try to commit again(XC)
818
if (!isActive()) {
819             throw ValidationException.cannotCommitUOWAgain();
820         }
821
822         if (isAfterWriteChangesFailed()) {
823             throw ValidationException.unitOfWorkAfterWriteChangesFailed("commit");
824         }
825
826         if (!isNestedUnitOfWork()) {
827             if (isSynchronized()) {
828                 // JTA synchronized units of work, cannot be resumed as there is no
829
// JTA transaction to register with after the commit,
830
// technically this could be supported if the uow started the transaction,
831
// but currently the after completion releases the uow and client session so not really possible.
832
throw ValidationException.cannotCommitAndResumeSynchronizedUOW(this);
833             }
834         }
835         if (getLifecycle() == CommitTransactionPending) {
836             commitAndResumeAfterWriteChanges();
837             return;
838         }
839         log(SessionLog.FINER, SessionLog.TRANSACTION, "begin_unit_of_work_commit");// bjv - correct spelling
840
getEventManager().preCommitUnitOfWork();
841         setLifecycle(CommitPending);
842         commitRootUnitOfWork();
843         getEventManager().postCommitUnitOfWork();
844         log(SessionLog.FINER, SessionLog.TRANSACTION, "end_unit_of_work_commit");
845         
846         log(SessionLog.FINER, SessionLog.TRANSACTION, "resume_unit_of_work");
847         synchronizeAndResume();
848         getEventManager().postResumeUnitOfWork();
849     }
850
851     /**
852      * INTERNAL:
853      * This method is used by the MappingWorkbench for their read-only file feature
854      * this method must not be exposed to or used by customers until it has been revised
855      * and the feature revisited to support OptimisticLocking and Serialization
856      */

857     public void commitAndResumeWithPreBuiltChangeSet(UnitOfWorkChangeSet uowChangeSet) throws DatabaseException, OptimisticLockException {
858         if (!isNestedUnitOfWork()) {
859             if (isSynchronized()) {
860                 // If we started the JTS transaction then we have to commit it as well.
861
if (getParent().wasJTSTransactionInternallyStarted()) {
862                     commitInternallyStartedExternalTransaction();
863                 }
864
865                 // Do not commit until the JTS wants to.
866
return;
867             }
868         }
869         log(SessionLog.FINER, SessionLog.TRANSACTION, "begin_unit_of_work_commit");// bjv - correct spelling
870
getEventManager().preCommitUnitOfWork();
871         setLifecycle(CommitPending);
872         commitRootUnitOfWorkWithPreBuiltChangeSet(uowChangeSet);
873         getEventManager().postCommitUnitOfWork();
874         log(SessionLog.FINER, SessionLog.TRANSACTION, "end_unit_of_work_commit");
875         log(SessionLog.FINER, SessionLog.TRANSACTION, "resume_unit_of_work");
876
877         synchronizeAndResume();
878         getEventManager().postResumeUnitOfWork();
879     }
880
881     /**
882      * PUBLIC:
883      * Commit the unit of work to its parent.
884      * For a nested unit of work this will merge any changes to its objects
885      * with its parents.
886      * For a first level unit of work it will commit all changes to its objects
887      * to the database as a single transaction. If successful the changes to its
888      * objects will be merged to its parent's objects. If the commit fails the database
889      * transaction will be rolledback, but the unit of work will remain active.
890      * It can then be retried or released.
891      * The normal commit failure releases the unit of work, forcing a new one to be acquired if further changes are desired.
892      * The resuming feature allows for the same unit of work (and working copies) to be continued to be used if an error occurs.
893      *
894      * @see #commit()
895      * @see #release()
896      */

897     public void commitAndResumeOnFailure() throws DatabaseException, OptimisticLockException {
898         // First clone the identity map, on failure replace the clone back as the cache.
899
IdentityMapManager failureManager = (IdentityMapManager)getIdentityMapAccessorInstance().getIdentityMapManager().clone();
900         try {
901             // Call commitAndResume.
902
// Oct 13, 2000 - JED PRS #13551
903
// This method will always resume now. Calling commitAndResume will sync the cache
904
// if successful. This method will take care of resuming if a failure occurs
905
commitAndResume();
906         } catch (RuntimeException JavaDoc exception) {
907             //reset unitOfWorkChangeSet. Needed for ObjectChangeTrackingPolicy and DeferredChangeDetectionPolicy
908
setUnitOfWorkChangeSet(null);
909             getIdentityMapAccessorInstance().setIdentityMapManager(failureManager);
910             log(SessionLog.FINER, SessionLog.TRANSACTION, "resuming_unit_of_work_from_failure");
911             throw exception;
912         }
913     }
914
915     /**
916      * INTERNAL:
917      * Commits a UnitOfWork where the commit process has already been
918      * initiated by all call to writeChanges().
919      * <p>
920      * a.k.a finalizeCommit()
921      */

922     protected void commitAfterWriteChanges() {
923         commitTransactionAfterWriteChanges();
924         mergeClonesAfterCompletion();
925         setDead();
926         release();
927     }
928
929     /**
930      * INTERNAL:
931      * Commits and resumes a UnitOfWork where the commit process has already been
932      * initiated by all call to writeChanges().
933      * <p>
934      * a.k.a finalizeCommit()
935      */

936     protected void commitAndResumeAfterWriteChanges() {
937         commitTransactionAfterWriteChanges();
938         mergeClonesAfterCompletion();
939         log(SessionLog.FINER, SessionLog.TRANSACTION, "resume_unit_of_work");
940         synchronizeAndResume();
941         getEventManager().postResumeUnitOfWork();
942     }
943
944     /**
945      * PROTECTED:
946      * Used in commit and commit-like methods to commit
947      * internally started external transaction
948      */

949     protected boolean commitInternallyStartedExternalTransaction() {
950         boolean committed = false;
951         if (!getParent().isInTransaction() || (wasTransactionBegunPrematurely() && (getParent().getTransactionMutex().getDepth() == 1))) {
952             committed = getParent().commitExternalTransaction();
953         }
954         return committed;
955     }
956
957     /**
958      * INTERNAL:
959      * Commit the changes to any objects to the parent.
960      */

961     public void commitRootUnitOfWork() throws DatabaseException, OptimisticLockException {
962         commitToDatabaseWithChangeSet(true);
963
964         // Merge after commit
965
mergeChangesIntoParent();
966     }
967
968     /**
969      * INTERNAL:
970      * This method is used by the MappingWorkbench read-only files feature
971      * It will commit a pre-built unitofwork change set to the database
972      */

973     public void commitRootUnitOfWorkWithPreBuiltChangeSet(UnitOfWorkChangeSet uowChangeSet) throws DatabaseException, OptimisticLockException {
974         //new code no need to check old commit
975
commitToDatabaseWithPreBuiltChangeSet(uowChangeSet, true);
976
977         // Merge after commit
978
mergeChangesIntoParent();
979     }
980
981     /**
982      * INTERNAL:
983      * CommitChanges To The Database from a calculated changeSet
984      * @param commitTransaction false if called by writeChanges as intent is
985      * not to finalize the transaction.
986      */

987     protected void commitToDatabase(boolean commitTransaction) {
988         try {
989             //CR4202 - ported from 3.6.4
990
if (wasTransactionBegunPrematurely()) {
991                 // beginTransaction() has been already called
992
setWasTransactionBegunPrematurely(false);
993             } else {
994                 beginTransaction();
995             }
996
997             if(commitTransaction) {
998                 setWasNonObjectLevelModifyQueryExecuted(false);
999             }
1000            
1001            Vector deletedObjects = null;// PERF: Avoid deletion if nothing to delete.
1002
if (hasDeletedObjects()) {
1003                deletedObjects = new Vector(getDeletedObjects().size());
1004                for (Enumeration objects = getDeletedObjects().keys(); objects.hasMoreElements();) {
1005                    deletedObjects.addElement(objects.nextElement());
1006                }
1007            }
1008
1009            if (shouldPerformDeletesFirst) {
1010                if (hasDeletedObjects()) {
1011                    // This must go to the commit manager because uow overrides to do normal deletion.
1012
getCommitManager().deleteAllObjects(deletedObjects);
1013
1014                    // Clear change sets of the deleted object to avoid redundant updates.
1015
for (Enumeration objects = getObjectsDeletedDuringCommit().keys();
1016                             objects.hasMoreElements();) {
1017                        oracle.toplink.essentials.internal.sessions.ObjectChangeSet objectChangeSet = (oracle.toplink.essentials.internal.sessions.ObjectChangeSet)this.unitOfWorkChangeSet.getObjectChangeSetForClone(objects.nextElement());
1018                        if (objectChangeSet != null) {
1019                            objectChangeSet.clear();
1020                        }
1021                    }
1022                }
1023
1024                // Let the commit manager figure out how to write the objects
1025
super.writeAllObjectsWithChangeSet(this.unitOfWorkChangeSet);
1026                // Issue all the SQL for the ModifyAllQuery's, don't touch the cache though
1027
issueModifyAllQueryList();
1028            } else {
1029                // Let the commit manager figure out how to write the objects
1030
super.writeAllObjectsWithChangeSet(this.unitOfWorkChangeSet);
1031                if (hasDeletedObjects()) {
1032                    // This must go to the commit manager because uow overrides to do normal deletion.
1033
getCommitManager().deleteAllObjects(deletedObjects);
1034                }
1035
1036                // Issue all the SQL for the ModifyAllQuery's, don't touch the cache though
1037
issueModifyAllQueryList();
1038            }
1039
1040            // Issue prepare event.
1041
getEventManager().prepareUnitOfWork();
1042
1043            // writeChanges() does everything but this step.
1044
// do not lock objects unless we are at the commit s
1045
if (commitTransaction) {
1046                try{
1047                    // if we should be acquiring locks before commit let's do that here
1048
if (getDatasourceLogin().shouldSynchronizeObjectLevelReadWriteDatabase()){
1049                        setMergeManager(new MergeManager(this));
1050                        //If we are merging into the shared cache acquire all required locks before merging.
1051
getParent().getIdentityMapAccessorInstance().getWriteLockManager().acquireRequiredLocks(getMergeManager(), (UnitOfWorkChangeSet)getUnitOfWorkChangeSet());
1052                    }
1053                    commitTransaction();
1054                }catch (RuntimeException JavaDoc throwable){
1055                    if (getDatasourceLogin().shouldSynchronizeObjectLevelReadWriteDatabase() && (getMergeManager() != null)) {
1056                        // exception occurred durring the commit.
1057
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(getMergeManager());
1058                        this.setMergeManager(null);
1059                    }
1060                    throw throwable;
1061                }catch (Error JavaDoc throwable){
1062                    if (getDatasourceLogin().shouldSynchronizeObjectLevelReadWriteDatabase() && (getMergeManager() != null)) {
1063                        // exception occurred durring the commit.
1064
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(getMergeManager());
1065                        this.setMergeManager(null);
1066                    }
1067                    throw throwable;
1068                }
1069            }else{
1070                setWasTransactionBegunPrematurely(true);
1071            }
1072
1073        } catch (RuntimeException JavaDoc exception) {
1074            rollbackTransaction(commitTransaction);
1075            if (hasExceptionHandler()) {
1076                getExceptionHandler().handleException(exception);
1077            } else {
1078                throw exception;
1079            }
1080        }
1081    }
1082
1083    /**
1084     * INTERNAL:
1085     * Commit the changes to any objects to the parent.
1086     * @param commitTransaction false if called by writeChanges as intent is
1087     * not to finalize the transaction.
1088     */

1089    protected void commitToDatabaseWithChangeSet(boolean commitTransaction) throws DatabaseException, OptimisticLockException {
1090        try {
1091            startOperationProfile(SessionProfiler.UowCommit);
1092            // The sequence numbers are assigned outside of the commit transaction.
1093
// This improves concurrency, avoids deadlock and in the case of three-tier will
1094
// not leave invalid cached sequences on rollback.
1095
// Also must first set the commit manager active.
1096
getCommitManager().setIsActive(true);
1097            // This will assgin sequence numbers.
1098
IdentityHashtable allObjects = collectAndPrepareObjectsForCommit();
1099
1100            // Must clone because the commitManager will remove the objects from the collection
1101
// as the objects are written to the database.
1102
setAllClonesCollection((IdentityHashtable)allObjects.clone());
1103            // Iterate over each clone and let the object build merge to clones into the originals.
1104
// The change set may already exist if using change tracking.
1105
if (getUnitOfWorkChangeSet() == null) {
1106                setUnitOfWorkChangeSet(new UnitOfWorkChangeSet());
1107            }
1108            calculateChanges(getAllClones(), (UnitOfWorkChangeSet)getUnitOfWorkChangeSet());
1109
1110            // Bug 2834266 only commit to the database if changes were made, avoid begin/commit of transaction
1111
if (hasModifications()) {
1112                commitToDatabase(commitTransaction);
1113            } else {
1114                // CR#... need to commit the transaction if begun early.
1115
if (wasTransactionBegunPrematurely()) {
1116                    if (commitTransaction) {
1117                        // Must be set to false for release to know not to rollback.
1118
setWasTransactionBegunPrematurely(false);
1119                        setWasNonObjectLevelModifyQueryExecuted(false);
1120                        commitTransaction();
1121                    }
1122                }
1123                getCommitManager().setIsActive(false);
1124            }
1125            endOperationProfile(SessionProfiler.UowCommit);
1126        } catch (RuntimeException JavaDoc exception) {
1127            handleException((RuntimeException JavaDoc)exception);
1128        }
1129    }
1130
1131    /**
1132     * INTERNAL:
1133     * Commit pre-built changeSet to the database changest to the database.
1134     */

1135    protected void commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkChangeSet uowChangeSet, boolean commitTransaction) throws DatabaseException, OptimisticLockException {
1136        try {
1137            // The sequence numbers are assigned outside of the commit transaction.
1138
// This improves concurrency, avoids deadlock and in the case of three-tier will
1139
// not leave invalid cached sequences on rollback.
1140
// Also must first set the commit manager active.
1141
getCommitManager().setIsActive(true);
1142            //Set empty collection in allClones for merge.
1143
setAllClonesCollection(new IdentityHashtable());
1144            // Iterate over each clone and let the object build merge to clones into the originals.
1145
setUnitOfWorkChangeSet(uowChangeSet);
1146            commitToDatabase(commitTransaction);
1147
1148        } catch (RuntimeException JavaDoc exception) {
1149            handleException((RuntimeException JavaDoc)exception);
1150        }
1151    }
1152
1153    /**
1154     * INTERNAL:
1155     * This is internal to the uow, transactions should not be used explictly in a uow.
1156     * The uow shares its parents transactions.
1157     */

1158    public void commitTransaction() throws DatabaseException {
1159        getParent().commitTransaction();
1160    }
1161
1162    /**
1163     * INTERNAL:
1164     * After writeChanges() everything has been done except for committing
1165     * the transaction. This allows that execution path to 'catch up'.
1166     */

1167    protected void commitTransactionAfterWriteChanges() {
1168        setWasNonObjectLevelModifyQueryExecuted(false);
1169        if (hasModifications() || wasTransactionBegunPrematurely()) {
1170             try{
1171                // if we should be acquiring locks before commit let's do that here
1172
if (getDatasourceLogin().shouldSynchronizeObjectLevelReadWriteDatabase() && (getUnitOfWorkChangeSet() != null)) {
1173                    setMergeManager(new MergeManager(this));
1174                    //If we are merging into the shared cache acquire all required locks before merging.
1175
getParent().getIdentityMapAccessorInstance().getWriteLockManager().acquireRequiredLocks(getMergeManager(), (UnitOfWorkChangeSet)getUnitOfWorkChangeSet());
1176                }
1177                setWasTransactionBegunPrematurely(false);
1178                commitTransaction();
1179            }catch (RuntimeException JavaDoc exception){
1180                if (getDatasourceLogin().shouldSynchronizeObjectLevelReadWriteDatabase() && (getMergeManager() != null)) {
1181                    // exception occurred durring the commit.
1182
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(getMergeManager());
1183                    this.setMergeManager(null);
1184                }
1185                rollbackTransaction();
1186                release();
1187                handleException(exception);
1188            }catch (Error JavaDoc throwable){
1189                if (getDatasourceLogin().shouldSynchronizeObjectLevelReadWriteDatabase() && (getMergeManager() != null)) {
1190                    // exception occurred durring the commit.
1191
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(getMergeManager());
1192                    this.setMergeManager(null);
1193                }
1194                throw throwable;
1195            }
1196        }
1197    }
1198
1199    /**
1200     * INTERNAL:
1201     * Copy the read only classes from the unit of work.
1202     */

1203    // Added Nov 8, 2000 JED for Patch 2.5.1.8, Ref: Prs 24502
1204
public Vector copyReadOnlyClasses() {
1205        return Helper.buildVectorFromHashtableElements(getReadOnlyClasses());
1206    }
1207
1208    /**
1209     * PUBLIC:
1210     * Merge the attributes of the clone into the unit of work copy.
1211     * This can be used for objects that are returned from the client through
1212     * RMI serialization or other serialization mechanisms, because the RMI object will
1213     * be a clone this will merge its attributes correctly to preserve object identity
1214     * within the unit of work and record its changes.
1215     * Everything connected to this object (i.e. the entire object tree where rmiClone
1216     * is the root) is also merged.
1217     *
1218     * @return the registered version for the clone being merged.
1219     * @see #mergeClone(Object)
1220     * @see #shallowMergeClone(Object)
1221     */

1222    public Object JavaDoc deepMergeClone(Object JavaDoc rmiClone) {
1223        return mergeClone(rmiClone, MergeManager.CASCADE_ALL_PARTS);
1224    }
1225
1226    /**
1227     * PUBLIC:
1228     * Revert the object's attributes from the parent.
1229     * This reverts everything the object references.
1230     *
1231     * @return the object reverted.
1232     * @see #revertObject(Object)
1233     * @see #shallowRevertObject(Object)
1234     */

1235    public Object JavaDoc deepRevertObject(Object JavaDoc clone) {
1236        return revertObject(clone, MergeManager.CASCADE_ALL_PARTS);
1237    }
1238
1239    /**
1240     * ADVANCED:
1241     * Unregister the object with the unit of work.
1242     * This can be used to delete an object that was just created and is not yet persistent.
1243     * Delete object can also be used, but will result in inserting the object and then deleting it.
1244     * The method should be used carefully because it will delete all the reachable parts.
1245     */

1246    public void deepUnregisterObject(Object JavaDoc clone) {
1247        unregisterObject(clone, DescriptorIterator.CascadeAllParts);
1248    }
1249
1250    /**
1251     * PUBLIC:
1252     * Delete all of the objects and all of their privately owned parts in the database.
1253     * Delete operations are delayed in a unit of work until commit.
1254     */

1255    public void deleteAllObjects(Vector domainObjects) {
1256        // This must be overriden to avoid dispatching to the commit manager.
1257
for (Enumeration objectsEnum = domainObjects.elements(); objectsEnum.hasMoreElements();) {
1258            deleteObject(objectsEnum.nextElement());
1259        }
1260    }
1261
1262    /**
1263     * INTERNAL:
1264     * Search for any objects in the parent that have not been registered.
1265     * These are required so that the nested unit of work does not add them to the parent
1266     * clone mapping on commit, causing possible incorrect insertions if they are dereferenced.
1267     */

1268    protected void discoverAllUnregisteredNewObjects() {
1269        // 2612538 - the default size of IdentityHashtable (32) is appropriate
1270
IdentityHashtable visitedNodes = new IdentityHashtable();
1271        IdentityHashtable newObjects = new IdentityHashtable();
1272        IdentityHashtable existingObjects = new IdentityHashtable();
1273
1274        // Iterate over each clone.
1275
for (Enumeration clonesEnum = getCloneMapping().keys(); clonesEnum.hasMoreElements();) {
1276            Object JavaDoc clone = clonesEnum.nextElement();
1277            discoverUnregisteredNewObjects(clone, newObjects, existingObjects, visitedNodes);
1278        }
1279
1280        setUnregisteredNewObjects(newObjects);
1281        setUnregisteredExistingObjects(existingObjects);
1282    }
1283
1284    /**
1285     * INTERNAL:
1286     * Traverse the object to find references to objects not registered in this unit of work.
1287     */

1288    public void discoverUnregisteredNewObjects(Object JavaDoc clone, IdentityHashtable knownNewObjects, IdentityHashtable unregisteredExistingObjects, IdentityHashtable visitedObjects) {
1289        // This define an inner class for process the itteration operation, don't be scared, its just an inner class.
1290
DescriptorIterator iterator = new DescriptorIterator() {
1291            public void iterate(Object JavaDoc object) {
1292                // If the object is read-only the do not continue the traversal.
1293
if (isClassReadOnly(object.getClass(), this.getCurrentDescriptor())) {
1294                    this.setShouldBreak(true);
1295                    return;
1296                }
1297
1298                /* CR3440: Steven Vo
1299                 * Include the case that object is original then do nothing
1300                 */

1301                if (isSmartMerge() && isOriginalNewObject(object)) {
1302                    return;
1303                } else if (!isObjectRegistered(object)) {// Don't need to check for aggregates, as iterator does not iterate on them by default.
1304
if ((shouldPerformNoValidation()) && (checkForUnregisteredExistingObject(object))) {
1305                        // If no validation is performed and the object exists we need
1306
// To keep a record of this object to ignore it, also I need to
1307
// Stop iterating over it.
1308
((IdentityHashtable)getUnregisteredExistingObjects()).put(object, object);
1309                        this.setShouldBreak(true);
1310                        return;
1311
1312                    }
1313
1314                    // This means it is a unregistered new object
1315
((IdentityHashtable)getResult()).put(object, object);
1316                }
1317            }
1318        };
1319
1320        //set the collection in the UnitofWork to be this list
1321
setUnregisteredExistingObjects(unregisteredExistingObjects);
1322
1323        iterator.setVisitedObjects(visitedObjects);
1324        iterator.setResult(knownNewObjects);
1325        iterator.setSession(this);
1326        // When using wrapper policy in EJB the iteration should stop on beans,
1327
// this is because EJB forces beans to be registered anyway and clone identity can be violated
1328
// and the violated clones references to session objects should not be traversed.
1329
iterator.setShouldIterateOverWrappedObjects(false);
1330        iterator.startIterationOn(clone);
1331    }
1332
1333    /**
1334     * ADVANCED:
1335     * The unit of work performs validations such as,
1336     * ensuring multiple copies of the same object don't exist in the same unit of work,
1337     * ensuring deleted objects are not refered after commit,
1338     * ensures that objects from the parent cache are not refered in the unit of work cache.
1339     * The level of validation can be increased or decreased for debugging purposes or under
1340     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
1341     * It is strongly suggested that clone identity not be violate in the unit of work.
1342     */

1343    public void dontPerformValidation() {
1344        setValidationLevel(None);
1345    }
1346
1347    /**
1348     * INTERNAL:
1349     * Override From session. Get the accessor based on the query, and execute call,
1350     * this is here for session broker.
1351     */

1352    public Object JavaDoc executeCall(Call call, AbstractRecord translationRow, DatabaseQuery query) throws DatabaseException {
1353        Accessor accessor;
1354        if (query.getSessionName() == null) {
1355            accessor = query.getSession().getAccessor(query.getReferenceClass());
1356        } else {
1357            accessor = query.getSession().getAccessor(query.getSessionName());
1358        }
1359
1360        query.setAccessor(accessor);
1361        try {
1362            return query.getAccessor().executeCall(call, translationRow, this);
1363        } finally {
1364            if (call.isFinished()) {
1365                query.setAccessor(null);
1366            }
1367        }
1368    }
1369
1370    /**
1371     * ADVANCED:
1372     * Set optmistic read lock on the object. This feature is overide by normal optimistic lock.
1373     * when the object is changed in UnitOfWork. The cloneFromUOW must be the clone of from this
1374     * UnitOfWork and it must implements version locking or timestamp locking.
1375     * The SQL would look like the followings.
1376     *
1377     * If shouldModifyVersionField is true,
1378     * "UPDATE EMPLOYEE SET VERSION = 2 WHERE EMP_ID = 9 AND VERSION = 1"
1379     *
1380     * If shouldModifyVersionField is false,
1381     * "UPDATE EMPLOYEE SET VERSION = 1 WHERE EMP_ID = 9 AND VERSION = 1"
1382     */

1383    public void forceUpdateToVersionField(Object JavaDoc lockObject, boolean shouldModifyVersionField) {
1384        ClassDescriptor descriptor = getDescriptor(lockObject);
1385        if (descriptor == null) {
1386            throw DescriptorException.missingDescriptor(lockObject.getClass().toString());
1387        }
1388        getOptimisticReadLockObjects().put(descriptor.getObjectBuilder().unwrapObject(lockObject, this), new Boolean JavaDoc(shouldModifyVersionField));
1389    }
1390
1391    /**
1392     * INTERNAL:
1393     * The uow does not store a local accessor but shares its parents.
1394     */

1395    public Accessor getAccessor() {
1396        return getParent().getAccessor();
1397    }
1398
1399    /**
1400     * INTERNAL:
1401     * The commit manager is used to resolve referncial integrity on commits of multiple objects.
1402     * The commit manage is lazy init from parent.
1403     */

1404    public CommitManager getCommitManager() {
1405        // PERF: lazy init, not always required for release/commit with no changes.
1406
if (commitManager == null) {
1407            commitManager = new CommitManager(this);
1408            // Initialize the commit manager
1409
commitManager.setCommitOrder(getParent().getCommitManager().getCommitOrder());
1410        }
1411        return commitManager;
1412    }
1413
1414    /**
1415     * INTERNAL:
1416     * The uow does not store a local accessor but shares its parents.
1417     */

1418    public Accessor getAccessor(Class JavaDoc domainClass) {
1419        return getParent().getAccessor(domainClass);
1420    }
1421
1422    /**
1423     * INTERNAL:
1424     * The uow does not store a local accessor but shares its parents.
1425     */

1426    public Accessor getAccessor(String JavaDoc sessionName) {
1427        return getParent().getAccessor(sessionName);
1428    }
1429
1430    /**
1431     * PUBLIC:
1432     * Return the active unit of work for the current active external (JTS) transaction.
1433     * This should only be used with JTS and will return null if no external transaction exists.
1434     */

1435    public oracle.toplink.essentials.sessions.UnitOfWork getActiveUnitOfWork() {
1436
1437        /* Steven Vo: CR# 2517
1438           This fixed the problem of returning null when this method is called on a UOW.
1439           UOW does not copy the parent session's external transaction controller
1440           when it is acquired but session does */

1441        return getParent().getActiveUnitOfWork();
1442    }
1443
1444    /**
1445     * INTERNAL:
1446     * This method is used to get a copy of the collection of all clones in the UnitOfWork
1447     * @return oracle.toplink.essentials.internal.helper.IdentityHashtable
1448     */

1449    protected IdentityHashtable getAllClones() {
1450        return this.allClones;
1451    }
1452
1453    /**
1454     * INTERNAL:
1455     * Return any new objects matching the expression.
1456     * Used for in-memory querying.
1457     */

1458    public Vector getAllFromNewObjects(Expression selectionCriteria, Class JavaDoc theClass, AbstractRecord translationRow, InMemoryQueryIndirectionPolicy valueHolderPolicy) {
1459        // If new object are in the cache then they will have already been queried.
1460
if (shouldNewObjectsBeCached()) {
1461            return new Vector(1);
1462        }
1463
1464        // PERF: Avoid initialization of new objects if none.
1465
if (!hasNewObjects()) {
1466            return new Vector(1);
1467        }
1468
1469        Vector objects = new Vector();
1470        for (Enumeration newObjectsEnum = getNewObjectsOriginalToClone().elements();
1471                 newObjectsEnum.hasMoreElements();) {
1472            Object JavaDoc object = newObjectsEnum.nextElement();
1473            if (theClass.isInstance(object)) {
1474                if (selectionCriteria == null) {
1475                    objects.addElement(object);
1476                } else if (selectionCriteria.doesConform(object, this, translationRow, valueHolderPolicy)) {
1477                    objects.addElement(object);
1478                }
1479            }
1480        }
1481        return objects;
1482    }
1483
1484    /**
1485     * INTERNAL:
1486     * Return the backup clone for the working clone.
1487     */

1488    public Object JavaDoc getBackupClone(Object JavaDoc clone) throws QueryException {
1489        Object JavaDoc backupClone = getCloneMapping().get(clone);
1490        if (backupClone != null) {
1491            return backupClone;
1492        }
1493
1494        /* CR3440: Steven Vo
1495         * Smart merge if neccessary in isObjectRegistered()
1496         */

1497        if (isObjectRegistered(clone)) {
1498            return getCloneMapping().get(clone);
1499
1500        } else {
1501            ClassDescriptor descriptor = getDescriptor(clone);
1502            Vector primaryKey = keyFromObject(clone, descriptor);
1503
1504            // This happens if clone was from the parent identity map.
1505
if (getParent().getIdentityMapAccessorInstance().containsObjectInIdentityMap(primaryKey, clone.getClass(), descriptor)) {
1506                //cr 3796
1507
if ((getUnregisteredNewObjects().get(clone) != null) && isMergePending()) {
1508                    //Another thread has read the new object before it has had a chance to
1509
//merge this object.
1510
// It also means it is an unregistered new object, so create a new backup clone for it.
1511
return descriptor.getObjectBuilder().buildNewInstance();
1512                }
1513                if (hasObjectsDeletedDuringCommit() && getObjectsDeletedDuringCommit().containsKey(clone)) {
1514                    throw QueryException.backupCloneIsDeleted(clone);
1515                }
1516                throw QueryException.backupCloneIsOriginalFromParent(clone);
1517            }
1518            // Also check that the object is not the original to a registered new object
1519
// (the original should not be referenced if not smart merge, this is an error.
1520
else if (hasNewObjects() && getNewObjectsOriginalToClone().containsKey(clone)) {
1521
1522                /* CR3440: Steven Vo
1523                 * Check case that clone is original
1524                 */

1525                if (isSmartMerge()) {
1526                    backupClone = getCloneMapping().get(getNewObjectsOriginalToClone().get(clone));
1527
1528                } else {
1529                    throw QueryException.backupCloneIsOriginalFromSelf(clone);
1530                }
1531            } else {
1532                // This means it is an unregistered new object, so create a new backup clone for it.
1533
backupClone = descriptor.getObjectBuilder().buildNewInstance();
1534            }
1535        }
1536
1537        return backupClone;
1538    }
1539
1540    /**
1541     * INTERNAL:
1542     * Return the backup clone for the working clone.
1543     */

1544    public Object JavaDoc getBackupCloneForCommit(Object JavaDoc clone) {
1545        Object JavaDoc backupClone = getBackupClone(clone);
1546
1547        /* CR3440: Steven Vo
1548         * Build new instance only if it was not handled by getBackupClone()
1549         */

1550        if (isCloneNewObject(clone)) {
1551            return getDescriptor(clone).getObjectBuilder().buildNewInstance();
1552        }
1553
1554        return backupClone;
1555    }
1556
1557    /**
1558     * ADVANCED:
1559     * This method Will Calculate the chages for the UnitOfWork. Without assigning sequence numbers
1560     * This is a Computationaly intensive operation and should be avoided unless necessary.
1561     * A valid changeSet, with sequencenumbers can be collected from the UnitOfWork After the commit
1562     * is complete by calling unitOfWork.getUnitOfWorkChangeSet()
1563     */

1564    public oracle.toplink.essentials.changesets.UnitOfWorkChangeSet getCurrentChanges() {
1565        IdentityHashtable allObjects = null;
1566        allObjects = collectAndPrepareObjectsForNestedMerge();
1567        return calculateChanges(allObjects, new UnitOfWorkChangeSet());
1568    }
1569
1570    /**
1571     * INTERNAL:
1572     * Gets the next link in the chain of sessions followed by a query's check
1573     * early return, the chain of sessions with identity maps all the way up to
1574     * the root session.
1575     * <p>
1576     * Used for session broker which delegates to registered sessions, or UnitOfWork
1577     * which checks parent identity map also.
1578     * @param canReturnSelf true when method calls itself. If the path
1579     * starting at <code>this</code> is acceptable. Sometimes true if want to
1580     * move to the first valid session, i.e. executing on ClientSession when really
1581     * should be on ServerSession.
1582     * @param terminalOnly return the session we will execute the call on, not
1583     * the next step towards it.
1584     * @return this if there is no next link in the chain
1585     */

1586    public AbstractSession getParentIdentityMapSession(DatabaseQuery query, boolean canReturnSelf, boolean terminalOnly) {
1587        if (canReturnSelf && !terminalOnly) {
1588            return this;
1589        } else {
1590            return getParent().getParentIdentityMapSession(query, true, terminalOnly);
1591        }
1592    }
1593
1594    /**
1595     * INTERNAL:
1596     * Gets the session which this query will be executed on.
1597     * Generally will be called immediately before the call is translated,
1598     * which is immediately before session.executeCall.
1599     * <p>
1600     * Since the execution session also knows the correct datasource platform
1601     * to execute on, it is often used in the mappings where the platform is
1602     * needed for type conversion, or where calls are translated.
1603     * <p>
1604     * Is also the session with the accessor. Will return a ClientSession if
1605     * it is in transaction and has a write connection.
1606     * @return a session with a live accessor
1607     * @param query may store session name or reference class for brokers case
1608     */

1609    public AbstractSession getExecutionSession(DatabaseQuery query) {
1610        // This optimization is only for when executing with a ClientSession in
1611
// transaction. In that case log with the UnitOfWork instead of the
1612
// ClientSession.
1613
// Note that if actually executing on ServerSession or a registered
1614
// session of a broker, must execute on that session directly.
1615

1616        //bug 5201121 Always use the parent or execution session from the parent
1617
// should never use the unit of work as it does not controll the
1618
//accessors and with a sessioon broker it will not have the correct
1619
//login info
1620
return getParent().getExecutionSession(query);
1621    }
1622
1623    /**
1624     * INTERNAL:
1625     * Return the clone mapping.
1626     * The clone mapping contains clone of all registered objects,
1627     * this is required to store the original state of the objects when registered
1628     * so that only what is changed will be commited to the database and the parent,
1629     * (this is required to support parralel unit of work).
1630     */

1631    public IdentityHashtable getCloneMapping() {
1632        // PERF: lazy-init (3286089)
1633
if (cloneMapping == null) {
1634            // 2612538 - the default size of IdentityHashtable (32) is appropriate
1635
cloneMapping = new IdentityHashtable();
1636        }
1637        return cloneMapping;
1638    }
1639
1640    protected boolean hasCloneMapping() {
1641        return ((cloneMapping != null) && !cloneMapping.isEmpty());
1642    }
1643
1644    /**
1645     * INTERNAL:
1646     * Hashtable used to avoid garbage collection in weak caches.
1647     * ALSO, hashtable used as lookup when originals used for merge when original in
1648     * identitymap can not be found. As in a CacheIdentityMap
1649     */

1650    public IdentityHashtable getCloneToOriginals() {
1651        //Helper.toDo("proper fix, collection merge can have objects disapear for original.");
1652
if (cloneToOriginals == null) {// Must lazy initialize for remote.
1653
// 2612538 - the default size of IdentityHashtable (32) is appropriate
1654
cloneToOriginals = new IdentityHashtable();
1655        }
1656        return cloneToOriginals;
1657    }
1658
1659    protected boolean hasCloneToOriginals() {
1660        return ((cloneToOriginals != null) && !cloneToOriginals.isEmpty());
1661    }
1662
1663    /**
1664     * INTERNAL:
1665     * Return if there are any registered new objects.
1666     * This is used for both newObjectsOriginalToClone and newObjectsCloneToOriginal as they are always in synch.
1667     * PERF: Used to avoid initialization of new objects hashtable unless required.
1668     */

1669    public boolean hasNewObjects() {
1670        return ((newObjectsOriginalToClone != null) && !newObjectsOriginalToClone.isEmpty());
1671    }
1672
1673    /**
1674     * INTERNAL: Returns the set of read-only classes that gets assigned to each newly created UnitOfWork.
1675     *
1676     * @see oracle.toplink.essentials.sessions.Project#setDefaultReadOnlyClasses(Vector)
1677     */

1678    public Vector getDefaultReadOnlyClasses() {
1679        return getParent().getDefaultReadOnlyClasses();
1680    }
1681
1682    /**
1683     * INTERNAL:
1684     * The deleted objects stores any objects removed during the unit of work.
1685     * On commit they will all be removed from the database.
1686     */

1687    public IdentityHashtable getDeletedObjects() {
1688        if (deletedObjects == null) {
1689            // 2612538 - the default size of IdentityHashtable (32) is appropriate
1690
deletedObjects = new IdentityHashtable();
1691        }
1692        return deletedObjects;
1693    }
1694
1695    protected boolean hasDeletedObjects() {
1696        return ((deletedObjects != null) && !deletedObjects.isEmpty());
1697    }
1698
1699    /**
1700     * PUBLIC:
1701     * Return the descriptor for the alias.
1702     * UnitOfWork delegates this to the parent
1703     * Introduced because of Bug#2610803
1704     */

1705    public ClassDescriptor getDescriptorForAlias(String JavaDoc alias) {
1706        return getParent().getDescriptorForAlias(alias);
1707    }
1708
1709    /**
1710     * PUBLIC:
1711     * Return all registered descriptors.
1712     * The unit of work inherits its parent's descriptors. The each descriptor's Java Class
1713     * is used as the key in the Hashtable returned.
1714     */

1715    public Map getDescriptors() {
1716        return getParent().getDescriptors();
1717    }
1718
1719    /**
1720     * INTERNAL:
1721     * The life cycle tracks if the unit of work is active and is used for JTS.
1722     */

1723    public int getLifecycle() {
1724        return lifecycle;
1725    }
1726
1727    /**
1728     * A reference to the last used merge manager. This is used to track locked
1729     * objects.
1730     */

1731    public MergeManager getMergeManager() {
1732        return this.lastUsedMergeManager;
1733    }
1734
1735    /**
1736     * INTERNAL:
1737     * The hashtable stores any new aggregates that have been cloned.
1738     */

1739    public IdentityHashtable getNewAggregates() {
1740        if (this.newAggregates == null) {
1741            // 2612538 - the default size of IdentityHashtable (32) is appropriate
1742
this.newAggregates = new IdentityHashtable();
1743        }
1744        return newAggregates;
1745    }
1746
1747    /**
1748     * INTERNAL:
1749     * The new objects stores any objects newly created during the unit of work.
1750     * On commit they will all be inserted into the database.
1751     */

1752    public synchronized IdentityHashtable getNewObjectsCloneToOriginal() {
1753        if (newObjectsCloneToOriginal == null) {
1754            // 2612538 - the default size of IdentityHashtable (32) is appropriate
1755
newObjectsCloneToOriginal = new IdentityHashtable();
1756        }
1757        return newObjectsCloneToOriginal;
1758    }
1759
1760    /**
1761     * INTERNAL:
1762     * The new objects stores any objects newly created during the unit of work.
1763     * On commit they will all be inserted into the database.
1764     */

1765    public synchronized IdentityHashtable getNewObjectsOriginalToClone() {
1766        if (newObjectsOriginalToClone == null) {
1767            // 2612538 - the default size of IdentityHashtable (32) is appropriate
1768
newObjectsOriginalToClone = new IdentityHashtable();
1769        }
1770        return newObjectsOriginalToClone;
1771    }
1772
1773    /**
1774     * INTERNAL:
1775     * Return the Sequencing object used by the session.
1776     */

1777    public Sequencing getSequencing() {
1778        return getParent().getSequencing();
1779    }
1780
1781    /**
1782     * INTERNAL:
1783     * Marked internal as this is not customer API but helper methods for
1784     * accessing the server platform from within TopLink's other sessions types
1785     * (ie not DatabaseSession)
1786     */

1787    public ServerPlatform getServerPlatform(){
1788        return getParent().getServerPlatform();
1789    }
1790
1791    /**
1792     * INTERNAL:
1793     * Returns the type of session, its class.
1794     * <p>
1795     * Override to hide from the user when they are using an internal subclass
1796     * of a known class.
1797     * <p>
1798     * A user does not need to know that their UnitOfWork is a
1799     * non-deferred UnitOfWork, or that their ClientSession is an
1800     * IsolatedClientSession.
1801     */

1802    public String JavaDoc getSessionTypeString() {
1803        return "UnitOfWork";
1804    }
1805
1806    /**
1807     * INTERNAL:
1808     * Called after transaction is completed (committed or rolled back)
1809     */

1810    public void afterTransaction(boolean committed, boolean isExternalTransaction) {
1811        if (!committed && isExternalTransaction) {
1812            // In case jts transaction was internally started but rolled back
1813
// directly by TransactionManager this flag may still be true during afterCompletion
1814
getParent().setWasJTSTransactionInternallyStarted(false);
1815            //bug#4699614 -- added a new life cycle status so we know if the external transaction was rolledback and we don't try to rollback again later
1816
setLifecycle(AfterExternalTransactionRolledBack);
1817        }
1818        if ((getMergeManager() != null) && (getMergeManager().getAcquiredLocks() != null) && (!getMergeManager().getAcquiredLocks().isEmpty())) {
1819            //may have unreleased cache locks because of a rollback...
1820
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(getMergeManager());
1821            this.setMergeManager(null);
1822        }
1823        getParent().afterTransaction(committed, isExternalTransaction);
1824    }
1825
1826    /**
1827     * INTERNAL:
1828     * Return any new object matching the expression.
1829     * Used for in-memory querying.
1830     */

1831    public Object JavaDoc getObjectFromNewObjects(Class JavaDoc theClass, Vector selectionKey) {
1832        // PERF: Avoid initialization of new objects if none.
1833
if (!hasNewObjects()) {
1834            return null;
1835        }
1836        ObjectBuilder objectBuilder = getDescriptor(theClass).getObjectBuilder();
1837        for (Enumeration newObjectsEnum = getNewObjectsOriginalToClone().elements();
1838                 newObjectsEnum.hasMoreElements();) {
1839            Object JavaDoc object = newObjectsEnum.nextElement();
1840            if (theClass.isInstance(object)) {
1841                // removed dead null check as this method is never called if selectionKey == null
1842
Vector primaryKey = objectBuilder.extractPrimaryKeyFromObject(object, this);
1843                if (new CacheKey(primaryKey).equals(new CacheKey(selectionKey))) {
1844                    return object;
1845                }
1846            }
1847        }
1848        return null;
1849    }
1850
1851    /**
1852     * INTERNAL:
1853     * Return any new object matching the expression.
1854     * Used for in-memory querying.
1855     */

1856    public Object JavaDoc getObjectFromNewObjects(Expression selectionCriteria, Class JavaDoc theClass, AbstractRecord translationRow, InMemoryQueryIndirectionPolicy valueHolderPolicy) {
1857        // PERF: Avoid initialization of new objects if none.
1858
if (!hasNewObjects()) {
1859            return null;
1860        }
1861        for (Enumeration newObjectsEnum = getNewObjectsOriginalToClone().elements();
1862                 newObjectsEnum.hasMoreElements();) {
1863            Object JavaDoc object = newObjectsEnum.nextElement();
1864            if (theClass.isInstance(object)) {
1865                if (selectionCriteria == null) {
1866                    return object;
1867                }
1868                if (selectionCriteria.doesConform(object, this, translationRow, valueHolderPolicy)) {
1869                    return object;
1870                }
1871            }
1872        }
1873        return null;
1874    }
1875
1876    /**
1877     * INTERNAL:
1878     * Returns all the objects which are deleted during root commit of unit of work.
1879     */

1880    public IdentityHashtable getObjectsDeletedDuringCommit() {
1881        // PERF: lazy-init (3286089)
1882
if (objectsDeletedDuringCommit == null) {
1883            // 2612538 - the default size of IdentityHashtable (32) is appropriate
1884
objectsDeletedDuringCommit = new IdentityHashtable();
1885        }
1886        return objectsDeletedDuringCommit;
1887    }
1888
1889    protected boolean hasObjectsDeletedDuringCommit() {
1890        return ((objectsDeletedDuringCommit != null) && !objectsDeletedDuringCommit.isEmpty());
1891    }
1892
1893    /**
1894     * INTERNAL:
1895     * Return optimistic read lock objects
1896     */

1897    public Hashtable getOptimisticReadLockObjects() {
1898        if (optimisticReadLockObjects == null) {
1899            optimisticReadLockObjects = new Hashtable(2);
1900        }
1901        return optimisticReadLockObjects;
1902    }
1903
1904    /**
1905     * INTERNAL:
1906     * Return the original version of the new object (working clone).
1907     */

1908    public Object JavaDoc getOriginalVersionOfNewObject(Object JavaDoc workingClone) {
1909        // PERF: Avoid initialization of new objects if none.
1910
if (!hasNewObjects()) {
1911            return null;
1912        }
1913        return getNewObjectsCloneToOriginal().get(workingClone);
1914    }
1915
1916    /**
1917     * ADVANCED:
1918     * Return the original version of the object(clone) from the parent's identity map.
1919     */

1920    public Object JavaDoc getOriginalVersionOfObject(Object JavaDoc workingClone) {
1921        // Can be null when called from the mappings.
1922
if (workingClone == null) {
1923            return null;
1924        }
1925        ClassDescriptor descriptor = getDescriptor(workingClone);
1926        ObjectBuilder builder = descriptor.getObjectBuilder();
1927        Object JavaDoc implementation = builder.unwrapObject(workingClone, this);
1928
1929        Vector primaryKey = builder.extractPrimaryKeyFromObject(implementation, this);
1930        Object JavaDoc original = getParent().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, implementation.getClass(), descriptor, null);
1931
1932        if (original == null) {
1933            // Check if it is a registered new object.
1934
original = getOriginalVersionOfNewObject(implementation);
1935        }
1936
1937        if (original == null) {
1938            // For bug 3013948 looking in the cloneToOriginals mapping will not help
1939
// if the object was never registered.
1940
if (isClassReadOnly(implementation.getClass(), descriptor)) {
1941                return implementation;
1942            }
1943
1944            // The object could have been removed from the cache even though it was in the unit of work.
1945
// fix for 2.5.1.3 PWK (1360)
1946
if (hasCloneToOriginals()) {
1947                original = getCloneToOriginals().get(workingClone);
1948            }
1949        }
1950
1951        if (original == null) {
1952            // This means that it must be an unregistered new object, so register a new clone as its original.
1953
original = buildOriginal(implementation);
1954        }
1955
1956        return original;
1957    }
1958
1959    /**
1960     * ADVANCED:
1961     * Return the original version of the object(clone) from the parent's identity map.
1962     */

1963    public Object JavaDoc getOriginalVersionOfObjectOrNull(Object JavaDoc workingClone) {
1964        // Can be null when called from the mappings.
1965
if (workingClone == null) {
1966            return null;
1967        }
1968        ClassDescriptor descriptor = getDescriptor(workingClone);
1969        ObjectBuilder builder = descriptor.getObjectBuilder();
1970        Object JavaDoc implementation = builder.unwrapObject(workingClone, this);
1971
1972        Vector primaryKey = builder.extractPrimaryKeyFromObject(implementation, this);
1973        Object JavaDoc original = getParent().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, implementation.getClass(), descriptor, null);
1974
1975        if (original == null) {
1976            // Check if it is a registered new object.
1977
original = getOriginalVersionOfNewObject(implementation);
1978        }
1979
1980        if (original == null) {
1981            // For bug 3013948 looking in the cloneToOriginals mapping will not help
1982
// if the object was never registered.
1983
if (isClassReadOnly(implementation.getClass(), descriptor)) {
1984                return implementation;
1985            }
1986
1987            // The object could have been removed from the cache even though it was in the unit of work.
1988
// fix for 2.5.1.3 PWK (1360)
1989
if (hasCloneToOriginals()) {
1990                original = getCloneToOriginals().get(workingClone);
1991            }
1992        }
1993        return original;
1994    }
1995
1996    /**
1997     * PUBLIC:
1998     * Return the parent.
1999     * This is a unit of work if nested, otherwise a database session or client session.
2000     */

2001    public AbstractSession getParent() {
2002        return parent;
2003    }
2004
2005    /**
2006     * INTERNAL:
2007     * Return the platform for a particular class.
2008     */

2009    public Platform getPlatform(Class JavaDoc domainClass) {
2010        return getParent().getPlatform(domainClass);
2011    }
2012
2013    /**
2014     * INTERNAL:
2015     * Return whether to throw exceptions on conforming queries
2016     */

2017    public int getShouldThrowConformExceptions() {
2018        return shouldThrowConformExceptions;
2019    }
2020
2021    /**
2022     * PUBLIC:
2023     * Return the query from the session pre-defined queries with the given name.
2024     * This allows for common queries to be pre-defined, reused and executed by name.
2025     */

2026    public DatabaseQuery getQuery(String JavaDoc name, Vector arguments) {
2027        DatabaseQuery query = super.getQuery(name, arguments);
2028        if (query == null) {
2029            query = getParent().getQuery(name, arguments);
2030        }
2031
2032        return query;
2033    }
2034
2035    /**
2036     * PUBLIC:
2037     * Return the query from the session pre-defined queries with the given name.
2038     * This allows for common queries to be pre-defined, reused and executed by name.
2039     */

2040    public DatabaseQuery getQuery(String JavaDoc name) {
2041        DatabaseQuery query = super.getQuery(name);
2042        if (query == null) {
2043            query = getParent().getQuery(name);
2044        }
2045
2046        return query;
2047    }
2048
2049    /**
2050     * INTERNAL:
2051     * Returns the set of read-only classes for the receiver.
2052     * Use this method with setReadOnlyClasses() to modify a UnitOfWork's set of read-only
2053     * classes before using the UnitOfWork.
2054     * @return Hashtable containing the Java Classes that are currently read-only.
2055     * @see #setReadOnlyClasses(Vector)
2056     */

2057    public Hashtable getReadOnlyClasses() {
2058        return readOnlyClasses;
2059    }
2060
2061    /**
2062     * INTERNAL:
2063     * The removed objects stores any newly registered objects removed during the nested unit of work.
2064     * On commit they will all be removed from the parent unit of work.
2065     */

2066    protected IdentityHashtable getRemovedObjects() {
2067        // PERF: lazy-init (3286089)
2068
if (removedObjects == null) {
2069            // 2612538 - the default size of IdentityHashtable (32) is appropriate
2070
removedObjects = new IdentityHashtable();
2071        }
2072        return removedObjects;
2073    }
2074
2075    protected boolean hasRemovedObjects() {
2076        return ((removedObjects != null) && !removedObjects.isEmpty());
2077    }
2078
2079    protected boolean hasModifyAllQueries() {
2080        return ((modifyAllQueries != null) && !modifyAllQueries.isEmpty());
2081    }
2082
2083    protected boolean hasDeferredModifyAllQueries() {
2084        return ((deferredModifyAllQueries != null) && !deferredModifyAllQueries.isEmpty());
2085    }
2086
2087    /**
2088     * INTERNAL:
2089     * Find out what the lifecycle state of this UoW is in.
2090     */

2091    public int getState() {
2092        return lifecycle;
2093    }
2094    
2095    /**
2096     * INTERNAL:
2097     * PERF: Return the associated external transaction.
2098     * Used to optimize activeUnitOfWork lookup.
2099     */

2100    public Object JavaDoc getTransaction() {
2101        return transaction;
2102    }
2103    
2104    /**
2105     * INTERNAL:
2106     * PERF: Set the associated external transaction.
2107     * Used to optimize activeUnitOfWork lookup.
2108     */

2109    public void setTransaction(Object JavaDoc transaction) {
2110        this.transaction = transaction;
2111    }
2112
2113    /**
2114     * ADVANCED:
2115     * Returns the currentChangeSet from the UnitOfWork.
2116     * This is only valid after the UnitOfWOrk has commited successfully
2117     */

2118    public oracle.toplink.essentials.changesets.UnitOfWorkChangeSet getUnitOfWorkChangeSet() {
2119        return unitOfWorkChangeSet;
2120    }
2121
2122    /**
2123     * INTERNAL:
2124     * Used to lazy Initialize the unregistered existing Objects collection.
2125     * @return oracle.toplink.essentials.internal.helper.IdentityHashtable
2126     */

2127    public oracle.toplink.essentials.internal.helper.IdentityHashtable getUnregisteredExistingObjects() {
2128        if (this.unregisteredExistingObjects == null) {
2129            // 2612538 - the default size of IdentityHashtable (32) is appropriate
2130
this.unregisteredExistingObjects = new IdentityHashtable();
2131        }
2132        return unregisteredExistingObjects;
2133    }
2134
2135    /**
2136     * INTERNAL:
2137     * This is used to store unregistred objects discovered in the parent so that the child
2138     * unit of work knows not to register them on commit.
2139     */

2140    protected IdentityHashtable getUnregisteredNewObjects() {
2141        if (unregisteredNewObjects == null) {
2142            // 2612538 - the default size of IdentityHashtable (32) is appropriate
2143
unregisteredNewObjects = new IdentityHashtable();
2144        }
2145        return unregisteredNewObjects;
2146    }
2147
2148    /**
2149     * ADVANCED:
2150     * The unit of work performs validations such as,
2151     * ensuring multiple copies of the same object don't exist in the same unit of work,
2152     * ensuring deleted objects are not refered after commit,
2153     * ensures that objects from the parent cache are not refered in the unit of work cache.
2154     * The level of validation can be increased or decreased for debugging purposes or under
2155     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
2156     * It is strongly suggested that clone identity not be violate in the unit of work.
2157     */

2158    public int getValidationLevel() {
2159        return validationLevel;
2160    }
2161
2162    /**
2163     * ADVANCED:
2164     * The Unit of work is capable of preprocessing to determine if any on the clone have been changed.
2165     * This is computationaly expensive and should be avoided on large object graphs.
2166     */

2167    public boolean hasChanges() {
2168        if (hasNewObjects()) {
2169            return true;
2170        }
2171        IdentityHashtable allObjects = collectAndPrepareObjectsForNestedMerge();
2172
2173        //Using the nested merge prevent the UnitOfWork from assigning sequence numbers
2174
if (!getUnregisteredNewObjects().isEmpty()) {
2175            return true;
2176        }
2177        if (hasDeletedObjects()) {
2178            return true;
2179        }
2180        UnitOfWorkChangeSet changeSet = calculateChanges(allObjects, new UnitOfWorkChangeSet());
2181        return changeSet.hasChanges();
2182    }
2183
2184    /**
2185     * INTERNAL:
2186     * Does this unit of work have any changes or anything that requires a write
2187     * to the database and a transaction to be started.
2188     * Should be called after changes are calculated internally by commit.
2189     * <p>
2190     * Note if a transaction was begun prematurely it still needs to be committed.
2191     */

2192    protected boolean hasModifications() {
2193        if (getUnitOfWorkChangeSet().hasChanges() || hasDeletedObjects() || hasModifyAllQueries() || hasDeferredModifyAllQueries() || ((oracle.toplink.essentials.internal.sessions.UnitOfWorkChangeSet)getUnitOfWorkChangeSet()).hasForcedChanges()) {
2194            return true;
2195        } else {
2196            return false;
2197        }
2198    }
2199
2200    /**
2201     * INTERNAL:
2202     * Set up the IdentityMapManager. This method allows subclasses of Session to override
2203     * the default IdentityMapManager functionality.
2204     */

2205    public void initializeIdentityMapAccessor() {
2206        this.identityMapAccessor = new UnitOfWorkIdentityMapAccessor(this, new IdentityMapManager(this));
2207    }
2208
2209    /**
2210     * INTERNAL:
2211     * Return the results from exeucting the database query.
2212     * the arguments should be a database row with raw data values.
2213     */

2214    public Object JavaDoc internalExecuteQuery(DatabaseQuery query, AbstractRecord databaseRow) throws DatabaseException, QueryException {
2215        if (!isActive()) {
2216            throw QueryException.querySentToInactiveUnitOfWork(query);
2217        }
2218        return query.executeInUnitOfWork(this, databaseRow);
2219    }
2220
2221    /**
2222     * INTERNAL:
2223     * Register the object with the unit of work.
2224     * This does not perform wrapping or unwrapping.
2225     * This is used for internal registration in the merge manager.
2226     */

2227    public Object JavaDoc internalRegisterObject(Object JavaDoc object, ClassDescriptor descriptor) {
2228        if (object == null) {
2229            return null;
2230        }
2231        if (descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
2232            throw ValidationException.cannotRegisterAggregateObjectInUnitOfWork(object.getClass());
2233        }
2234        Object JavaDoc registeredObject = checkIfAlreadyRegistered(object, descriptor);
2235        if (registeredObject == null) {
2236            registeredObject = checkExistence(object);
2237            if (registeredObject == null) {
2238                // This means that the object is not in the parent im, so was created under this unit of work.
2239
// This means that it must be new.
2240
registeredObject = cloneAndRegisterNewObject(object);
2241            }
2242        }
2243        return registeredObject;
2244    }
2245
2246    /**
2247     * PUBLIC:
2248     * Return if the unit of work is active. (i.e. has not been released).
2249     */

2250    public boolean isActive() {
2251        return !isDead();
2252    }
2253
2254    /**
2255     * PUBLIC:
2256     * Checks to see if the specified class or descriptor is read-only or not in this UnitOfWork.
2257     *
2258     * @return boolean, true if the class is read-only, false otherwise.
2259     */

2260    public boolean isClassReadOnly(Class JavaDoc theClass, ClassDescriptor descriptor) {
2261        if ((descriptor != null) && (descriptor.shouldBeReadOnly())) {
2262            return true;
2263        }
2264        if ((theClass != null) && getReadOnlyClasses().containsKey(theClass)) {
2265            return true;
2266        }
2267        return false;
2268    }
2269
2270    /**
2271     * INTERNAL:
2272     * Check if the object is already registered.
2273     */

2274    public boolean isCloneNewObject(Object JavaDoc clone) {
2275        // PERF: Avoid initialization of new objects if none.
2276
if (!hasNewObjects()) {
2277            return false;
2278        }
2279        return getNewObjectsCloneToOriginal().containsKey(clone);
2280    }
2281
2282    /**
2283     * INTERNAL:
2284     * Return if the unit of work is waiting to be committed or in the process of being committed.
2285     */

2286    public boolean isCommitPending() {
2287        return getLifecycle() == CommitPending;
2288    }
2289
2290    /**
2291     * INTERNAL:
2292     * Return if the unit of work is dead.
2293     */

2294    public boolean isDead() {
2295        return getLifecycle() == Death;
2296    }
2297
2298    /**
2299     * PUBLIC:
2300     * Return whether the session currently has a database transaction in progress.
2301     */

2302    public boolean isInTransaction() {
2303        return getParent().isInTransaction();
2304    }
2305
2306    /**
2307     * INTERNAL:
2308     * Return if the unit of work is waiting to be merged or in the process of being merged.
2309     */

2310    public boolean isMergePending() {
2311        return getLifecycle() == MergePending;
2312    }
2313
2314    /**
2315     * INTERNAL:
2316     * Has writeChanges() been attempted on this UnitOfWork? It may have
2317     * either suceeded or failed but either way the UnitOfWork is in a highly
2318     * restricted state.
2319     */

2320    public boolean isAfterWriteChangesButBeforeCommit() {
2321        return ((getLifecycle() == CommitTransactionPending) || (getLifecycle() == WriteChangesFailed));
2322    }
2323
2324    /**
2325     * INTERNAL:
2326     * Once writeChanges has failed all a user can do really is rollback.
2327     */

2328    protected boolean isAfterWriteChangesFailed() {
2329        return getLifecycle() == WriteChangesFailed;
2330    }
2331
2332    /**
2333     * PUBLIC:
2334     * Return whether this session is a nested unit of work or not.
2335     */

2336    public boolean isNestedUnitOfWork() {
2337        return false;
2338    }
2339
2340    /**
2341     * INTERNAL:
2342     * Return if the object has been deleted in this unit of work.
2343     */

2344    public boolean isObjectDeleted(Object JavaDoc object) {
2345        boolean isDeleted = false;
2346        if (hasDeletedObjects()) {
2347            isDeleted = getDeletedObjects().containsKey(object);
2348        }
2349        if (getParent().isUnitOfWork()) {
2350            return isDeleted || ((UnitOfWorkImpl)getParent()).isObjectDeleted(object);
2351        } else {
2352            return isDeleted;
2353        }
2354    }
2355
2356    /**
2357     * INTERNAL:
2358     * This method is used to determine if the clone is a new Object in the UnitOfWork
2359     */

2360    public boolean isObjectNew(Object JavaDoc clone) {
2361        //CR3678 - ported from 4.0
2362
return (isCloneNewObject(clone) || (!isObjectRegistered(clone) && !getReadOnlyClasses().contains(clone.getClass()) && !getUnregisteredExistingObjects().contains(clone)));
2363    }
2364
2365    /**
2366     * INTERNAL:
2367     * Return whether the clone object is already registered.
2368     */

2369    public boolean isObjectRegistered(Object JavaDoc clone) {
2370        if (getCloneMapping().containsKey(clone)) {
2371            return true;
2372        }
2373
2374        // We do smart merge here
2375
if (isSmartMerge()){
2376            ClassDescriptor descriptor = getDescriptor(clone);
2377            if (getParent().getIdentityMapAccessorInstance().containsObjectInIdentityMap(keyFromObject(clone, descriptor), clone.getClass(), descriptor) ) {
2378                mergeCloneWithReferences(clone);
2379
2380                // don't put clone in clone mapping since it would result in duplicate clone
2381
return true;
2382            }
2383        }
2384        return false;
2385    }
2386
2387    /**
2388     * INTERNAL:
2389     * Return whether the original object is new.
2390     * It was either registered as new or discovered as a new aggregate
2391     * within another new object.
2392     */

2393    public boolean isOriginalNewObject(Object JavaDoc original) {
2394        return (hasNewObjects() && getNewObjectsOriginalToClone().containsKey(original)) || getNewAggregates().containsKey(original);
2395    }
2396
2397    /**
2398     * INTERNAL:
2399     * Return the status of smart merge
2400     */

2401    public static boolean isSmartMerge() {
2402        return SmartMerge;
2403    }
2404
2405    /**
2406     * INTERNAL:
2407     * For synchronized units of work, dump SQL to database.
2408     * For cases where writes occur before the end of the transaction don't commit
2409     */

2410    public void issueSQLbeforeCompletion() {
2411        issueSQLbeforeCompletion(true);
2412    }
2413    
2414    /**
2415     * INTERNAL:
2416     * For synchronized units of work, dump SQL to database.
2417     * For cases where writes occur before the end of the transaction don't commit
2418     */

2419    public void issueSQLbeforeCompletion(boolean commitTransaction) {
2420        if (getLifecycle() == CommitTransactionPending) {
2421            commitTransactionAfterWriteChanges();
2422            return;
2423        }
2424        // CR#... call event and log.
2425
log(SessionLog.FINER, SessionLog.TRANSACTION, "begin_unit_of_work_commit");
2426        getEventManager().preCommitUnitOfWork();
2427        setLifecycle(CommitPending);
2428        commitToDatabaseWithChangeSet(commitTransaction);
2429    }
2430
2431    /**
2432       * INTERNAL:
2433       * Will notify all the deferred ModifyAllQuery's (excluding UpdateAllQuery's) and deferred UpdateAllQuery's to execute.
2434       */

2435    protected void issueModifyAllQueryList() {
2436        if (deferredModifyAllQueries != null) {
2437            for (int i = 0; i < deferredModifyAllQueries.size(); i++) {
2438                Object JavaDoc[] queries = (Object JavaDoc[])deferredModifyAllQueries.get(i);
2439                ModifyAllQuery query = (ModifyAllQuery)queries[0];
2440                AbstractRecord translationRow = (AbstractRecord)queries[1];
2441                getParent().executeQuery(query, translationRow);
2442            }
2443        }
2444    }
2445
2446    /**
2447     * INTERNAL:
2448     * Return if this session is a synchronized unit of work.
2449     */

2450    public boolean isSynchronized() {
2451        return isSynchronized;
2452    }
2453
2454    /**
2455     * PUBLIC:
2456     * Return if this session is a unit of work.
2457     */

2458    public boolean isUnitOfWork() {
2459        return true;
2460    }
2461
2462    /**
2463     * INTERNAL: Merge the changes to all objects to the parent.
2464     */

2465    protected void mergeChangesIntoParent() {
2466        UnitOfWorkChangeSet uowChangeSet = (UnitOfWorkChangeSet)getUnitOfWorkChangeSet();
2467        if (uowChangeSet == null) {
2468            // may be using the old commit prosess usesOldCommit()
2469
setUnitOfWorkChangeSet(new UnitOfWorkChangeSet());
2470            uowChangeSet = (UnitOfWorkChangeSet)getUnitOfWorkChangeSet();
2471            calculateChanges(getAllClones(), (UnitOfWorkChangeSet)getUnitOfWorkChangeSet());
2472        }
2473
2474        // 3286123 - if no work to be done, skip this part of uow.commit()
2475
if (hasModifications()) {
2476            setPendingMerge();
2477            startOperationProfile(SessionProfiler.Merge);
2478            // Ensure concurrency if cache isolation requires.
2479
getParent().getIdentityMapAccessorInstance().acquireWriteLock();
2480            MergeManager manager = getMergeManager();
2481            if (manager == null){
2482                // no MergeManager created for locks durring commit
2483
manager = new MergeManager(this);
2484            }
2485             
2486            try {
2487                if (!isNestedUnitOfWork()) {
2488                    preMergeChanges();
2489                }
2490
2491                // Must clone the clone mapping because entries can be added to it during the merging,
2492
// and that can lead to concurrency problems.
2493
getParent().getEventManager().preMergeUnitOfWorkChangeSet(uowChangeSet);
2494                if (!isNestedUnitOfWork() && getDatasourceLogin().shouldSynchronizeObjectLevelReadWrite()) {
2495                    setMergeManager(manager);
2496                    //If we are merging into the shared cache acquire all required locks before merging.
2497
getParent().getIdentityMapAccessorInstance().getWriteLockManager().acquireRequiredLocks(getMergeManager(), (UnitOfWorkChangeSet)getUnitOfWorkChangeSet());
2498                }
2499                Enumeration changeSetLists = ((UnitOfWorkChangeSet)getUnitOfWorkChangeSet()).getObjectChanges().elements();
2500                while (changeSetLists.hasMoreElements()) {
2501                    Hashtable objectChangesList = (Hashtable)((Hashtable)changeSetLists.nextElement()).clone();
2502                    if (objectChangesList != null) {// may be no changes for that class type.
2503
for (Enumeration pendingEnum = objectChangesList.elements();
2504                                 pendingEnum.hasMoreElements();) {
2505                            ObjectChangeSet changeSetToWrite = (ObjectChangeSet)pendingEnum.nextElement();
2506                            if (changeSetToWrite.hasChanges()) {
2507                                Object JavaDoc objectToWrite = changeSetToWrite.getUnitOfWorkClone();
2508
2509                                //bug#4154455 -- only merge into the shared cache if the object is new or if it already exists in the shared cache
2510
if (changeSetToWrite.isNew() || (getOriginalVersionOfObjectOrNull(objectToWrite) != null)) {
2511                                    manager.mergeChanges(objectToWrite, changeSetToWrite);
2512                                }
2513                            } else {
2514                                // if no 'real' changes to the object change set, remove it from the
2515
// list so it won't be unnecessarily sent via cache sync.
2516
uowChangeSet.removeObjectChangeSet(changeSetToWrite);
2517                            }
2518                        }
2519                    }
2520                }
2521
2522                // Notify the queries to merge into the shared cache
2523
if (modifyAllQueries != null) {
2524                    for (int i = 0; i < modifyAllQueries.size(); i++) {
2525                        ModifyAllQuery query = (ModifyAllQuery)modifyAllQueries.get(i);
2526                        query.setSession(getParent());// ensure the query knows which cache to update
2527
query.mergeChangesIntoSharedCache();
2528                    }
2529                }
2530
2531                if (isNestedUnitOfWork()) {
2532                    changeSetLists = ((UnitOfWorkChangeSet)getUnitOfWorkChangeSet()).getNewObjectChangeSets().elements();
2533                    while (changeSetLists.hasMoreElements()) {
2534                        IdentityHashtable objectChangesList = (IdentityHashtable)((IdentityHashtable)changeSetLists.nextElement()).clone();
2535                        if (objectChangesList != null) {// may be no changes for that class type.
2536
for (Enumeration pendingEnum = objectChangesList.elements();
2537                                     pendingEnum.hasMoreElements();) {
2538                                ObjectChangeSet changeSetToWrite = (ObjectChangeSet)pendingEnum.nextElement();
2539                                if (changeSetToWrite.hasChanges()) {
2540                                    Object JavaDoc objectToWrite = changeSetToWrite.getUnitOfWorkClone();
2541                                    manager.mergeChanges(objectToWrite, changeSetToWrite);
2542                                } else {
2543                                    // if no 'real' changes to the object change set, remove it from the
2544
// list so it won't be unnecessarily sent via cache sync.
2545
uowChangeSet.removeObjectChangeSet(changeSetToWrite);
2546                                }
2547                            }
2548                        }
2549                    }
2550                }
2551                if (!isNestedUnitOfWork()) {
2552                    //If we are merging into the shared cache release all of the locks that we acquired.
2553
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(manager);
2554                    setMergeManager(null);
2555
2556                    postMergeChanges();
2557                }
2558            } finally {
2559                if (!isNestedUnitOfWork() && !manager.getAcquiredLocks().isEmpty()) {
2560                    // if the locks have not already been released (!acquiredLocks.empty)
2561
// then there must have been an error, release all of the locks.
2562
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(manager);
2563                    setMergeManager(null);
2564                }
2565                getParent().getIdentityMapAccessorInstance().releaseWriteLock();
2566                getParent().getEventManager().postMergeUnitOfWorkChangeSet(uowChangeSet);
2567                endOperationProfile(SessionProfiler.Merge);
2568            }
2569        }
2570    }
2571
2572    /**
2573     * PUBLIC:
2574     * Merge the attributes of the clone into the unit of work copy.
2575     * This can be used for objects that are returned from the client through
2576     * RMI serialization (or another serialization mechanism), because the RMI object
2577     * will be a clone this will merge its attributes correctly to preserve object
2578     * identity within the unit of work and record its changes.
2579     *
2580     * The object and its private owned parts are merged.
2581     *
2582     * @return the registered version for the clone being merged.
2583     * @see #shallowMergeClone(Object)
2584     * @see #deepMergeClone(Object)
2585     */

2586    public Object JavaDoc mergeClone(Object JavaDoc rmiClone) {
2587        return mergeClone(rmiClone, MergeManager.CASCADE_PRIVATE_PARTS);
2588    }
2589
2590    /**
2591     * INTERNAL:
2592     * Merge the attributes of the clone into the unit of work copy.
2593     */

2594    public Object JavaDoc mergeClone(Object JavaDoc rmiClone, int cascadeDepth) {
2595        if (rmiClone == null) {
2596            return null;
2597        }
2598
2599        //CR#2272
2600
logDebugMessage(rmiClone, "merge_clone");
2601
2602        startOperationProfile(SessionProfiler.Merge);
2603        ObjectBuilder builder = getDescriptor(rmiClone).getObjectBuilder();
2604        Object JavaDoc implementation = builder.unwrapObject(rmiClone, this);
2605
2606        MergeManager manager = new MergeManager(this);
2607        manager.mergeCloneIntoWorkingCopy();
2608        manager.setCascadePolicy(cascadeDepth);
2609
2610        Object JavaDoc merged = null;
2611        try {
2612            merged = manager.mergeChanges(implementation, null);
2613        } catch (RuntimeException JavaDoc exception) {
2614            merged = handleException(exception);
2615        }
2616        endOperationProfile(SessionProfiler.Merge);
2617
2618        return merged;
2619    }
2620
2621    /**
2622     * INTERNAL:
2623     * for synchronized units of work, merge changes into parent
2624     */

2625    public void mergeClonesAfterCompletion() {
2626        mergeChangesIntoParent();
2627        // CR#... call event and log.
2628
getEventManager().postCommitUnitOfWork();
2629        log(SessionLog.FINER, SessionLog.TRANSACTION, "end_unit_of_work_commit");
2630    }
2631
2632    /**
2633     * PUBLIC:
2634     * Merge the attributes of the clone into the unit of work copy.
2635     * This can be used for objects that are returned from the client through
2636     * RMI serialization (or another serialization mechanism), because the RMI object
2637     * will be a clone this will merge its attributes correctly to preserve object
2638     * identity within the unit of work and record its changes.
2639     *
2640     * The object and its private owned parts are merged. This will include references from
2641     * dependent objects to independent objects.
2642     *
2643     * @return the registered version for the clone being merged.
2644     * @see #shallowMergeClone(Object)
2645     * @see #deepMergeClone(Object)
2646     */

2647    public Object JavaDoc mergeCloneWithReferences(Object JavaDoc rmiClone) {
2648        return this.mergeCloneWithReferences(rmiClone, MergeManager.CASCADE_PRIVATE_PARTS);
2649    }
2650    
2651
2652    /**
2653     * PUBLIC:
2654     * Merge the attributes of the clone into the unit of work copy.
2655     * This can be used for objects that are returned from the client through
2656     * RMI serialization (or another serialization mechanism), because the RMI object
2657     * will be a clone this will merge its attributes correctly to preserve object
2658     * identity within the unit of work and record its changes.
2659     *
2660     * The object and its private owned parts are merged. This will include references from
2661     * dependent objects to independent objects.
2662     *
2663     * @return the registered version for the clone being merged.
2664     * @see #shallowMergeClone(Object)
2665     * @see #deepMergeClone(Object)
2666     */

2667    public Object JavaDoc mergeCloneWithReferences(Object JavaDoc rmiClone, int cascadePolicy) {
2668
2669       if (rmiClone == null) {
2670            return null;
2671        }
2672        ClassDescriptor descriptor = getDescriptor(rmiClone);
2673        if ((descriptor == null) || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
2674            if (cascadePolicy == MergeManager.CASCADE_BY_MAPPING){
2675                throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[]{rmiClone}));
2676            }
2677            return rmiClone;
2678        }
2679
2680        //CR#2272
2681
logDebugMessage(rmiClone, "merge_clone_with_references");
2682
2683        ObjectBuilder builder = descriptor.getObjectBuilder();
2684        Object JavaDoc implementation = builder.unwrapObject(rmiClone, this);
2685
2686        MergeManager manager = new MergeManager(this);
2687        manager.mergeCloneWithReferencesIntoWorkingCopy();
2688        manager.setCascadePolicy(cascadePolicy);
2689        Object JavaDoc mergedObject = manager.mergeChanges(implementation, null);
2690        if (isSmartMerge()) {
2691            return builder.wrapObject(mergedObject, this);
2692        } else {
2693            return mergedObject;
2694        }
2695    }
2696
2697    /**
2698     * PUBLIC:
2699     * Return a new instance of the class registered in this unit of work.
2700     * This can be used to ensure that new objects are registered correctly.
2701     */

2702    public Object JavaDoc newInstance(Class JavaDoc theClass) {
2703        //CR#2272
2704
logDebugMessage(theClass, "new_instance");
2705
2706        ClassDescriptor descriptor = getDescriptor(theClass);
2707        Object JavaDoc newObject = descriptor.getObjectBuilder().buildNewInstance();
2708        return registerObject(newObject);
2709    }
2710
2711    /**
2712     * INTERNAL:
2713     * This method will perform a delete operation on the provided objects pre-determing
2714     * the objects that will be deleted by a commit of the UnitOfWork including privately
2715     * owned objects. It does not execute a query for the deletion of these objects as the
2716     * normal deleteobject operation does. Mainly implemented to provide EJB 3.0 deleteObject
2717     * support.
2718     */

2719    public void performRemove(Object JavaDoc toBeDeleted, IdentityHashtable visitedObjects){
2720        try {
2721            if (toBeDeleted == null) {
2722                return;
2723            }
2724            ClassDescriptor descriptor = getDescriptor(toBeDeleted);
2725            if ((descriptor == null) || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
2726                throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[]{toBeDeleted}));
2727            }
2728            logDebugMessage(toBeDeleted, "deleting_object");
2729
2730            startOperationProfile(SessionProfiler.DeletedObject);
2731            //bug 4568370+4599010; fix EntityManager.remove() to handle new objects
2732
if (getDeletedObjects().contains(toBeDeleted)){
2733              return;
2734            }
2735            visitedObjects.put(toBeDeleted,toBeDeleted);
2736            Object JavaDoc registeredObject = checkIfAlreadyRegistered(toBeDeleted, descriptor);
2737            if (registeredObject == null) {
2738                Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(toBeDeleted, this);
2739                DoesExistQuery existQuery = descriptor.getQueryManager().getDoesExistQuery();
2740                existQuery = (DoesExistQuery)existQuery.clone();
2741                existQuery.setObject(toBeDeleted);
2742                existQuery.setPrimaryKey(primaryKey);
2743                existQuery.setDescriptor(descriptor);
2744        
2745                existQuery.setCheckCacheFirst(true);
2746                if (((Boolean JavaDoc)executeQuery(existQuery)).booleanValue()){
2747                    throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("cannot_remove_detatched_entity", new Object JavaDoc[]{toBeDeleted}));
2748                }//else, it is a new or previously deleted object that should be ignored (and delete should cascade)
2749
}else{
2750                //fire events only if this is a managed object
2751
if (descriptor.getEventManager().hasAnyEventListeners()) {
2752                    oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(toBeDeleted);
2753                    event.setEventCode(DescriptorEventManager.PreRemoveEvent);
2754                    event.setSession(this);
2755                    descriptor.getEventManager().executeEvent(event);
2756                }
2757                if (hasNewObjects() && getNewObjectsOriginalToClone().contains(registeredObject)){
2758                    unregisterObject(registeredObject, DescriptorIterator.NoCascading);
2759                }else{
2760                    getDeletedObjects().put(toBeDeleted, toBeDeleted);
2761                }
2762            }
2763            descriptor.getObjectBuilder().cascadePerformRemove(toBeDeleted, this, visitedObjects);
2764        } finally {
2765            endOperationProfile(SessionProfiler.DeletedObject);
2766        }
2767    }
2768
2769    /**
2770     * ADVANCED:
2771     * The unit of work performs validations such as,
2772     * ensuring multiple copies of the same object don't exist in the same unit of work,
2773     * ensuring deleted objects are not refered after commit,
2774     * ensures that objects from the parent cache are not refered in the unit of work cache.
2775     * The level of validation can be increased or decreased for debugging purposes or under
2776     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
2777     * It is strongly suggested that clone identity not be violate in the unit of work.
2778     */

2779    public void performFullValidation() {
2780        setValidationLevel(Full);
2781
2782    }
2783
2784    /**
2785     * ADVANCED:
2786     * The unit of work performs validations such as,
2787     * ensuring multiple copies of the same object don't exist in the same unit of work,
2788     * ensuring deleted objects are not refered after commit,
2789     * ensures that objects from the parent cache are not refered in the unit of work cache.
2790     * The level of validation can be increased or decreased for debugging purposes or under
2791     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
2792     * It is strongly suggested that clone identity not be violate in the unit of work.
2793     */

2794    public void performPartialValidation() {
2795        setValidationLevel(Partial);
2796    }
2797
2798    /**
2799     * INTERNAL:
2800     * This method is called from clone and register. It includes the processing
2801     * required to clone an object, including populating attributes, putting in
2802     * UOW identitymap and building a backupclone
2803     */

2804    protected void populateAndRegisterObject(Object JavaDoc original, Object JavaDoc workingClone, Vector primaryKey, ClassDescriptor descriptor, Object JavaDoc writeLockValue, long readTime, JoinedAttributeManager joinedAttributeManager) {
2805        // This must be registered before it is built to avoid cycles.
2806
getIdentityMapAccessorInstance().putInIdentityMap(workingClone, primaryKey, writeLockValue, readTime, descriptor);
2807
2808        //Set ChangeListener for ObjectChangeTrackingPolicy and AttributeChangeTrackingPolicy,
2809
//but not DeferredChangeDetectionPolicy. Build backup clone for DeferredChangeDetectionPolicy
2810
//or ObjectChangeTrackingPolicy, but not for AttributeChangeTrackingPolicy.
2811
// - Set listener before populating attributes so aggregates can find the parent's listener
2812
descriptor.getObjectChangePolicy().setChangeListener(workingClone, this, descriptor);
2813        descriptor.getObjectChangePolicy().dissableEventProcessing(workingClone);
2814
2815        ObjectBuilder builder = descriptor.getObjectBuilder();
2816        builder.populateAttributesForClone(original, workingClone, this, joinedAttributeManager);
2817        Object JavaDoc backupClone = descriptor.getObjectChangePolicy().buildBackupClone(workingClone, builder, this);
2818        getCloneMapping().put(workingClone, backupClone);
2819        
2820        descriptor.getObjectChangePolicy().enableEventProcessing(workingClone);
2821    }
2822
2823    /**
2824     * INTERNAL:
2825     * Remove objects from parent's identity map.
2826     */

2827    protected void postMergeChanges() {
2828        //bug 4730595: objects removed during flush are not removed from the cache during commit
2829
if (!this.getUnitOfWorkChangeSet().getDeletedObjects().isEmpty()){
2830            oracle.toplink.essentials.internal.helper.IdentityHashtable deletedObjects = this.getUnitOfWorkChangeSet().getDeletedObjects();
2831            for (Enumeration removedObjects = deletedObjects.keys(); removedObjects.hasMoreElements(); ) {
2832                ObjectChangeSet removedObjectChangeSet = (ObjectChangeSet) removedObjects.nextElement();
2833                java.util.Vector JavaDoc primaryKeys = removedObjectChangeSet.getPrimaryKeys();
2834                getParent().getIdentityMapAccessor().removeFromIdentityMap(primaryKeys, removedObjectChangeSet.getClassType(this));
2835            }
2836        }
2837    }
2838
2839    /**
2840     * INTERNAL:
2841     * Remove objects deleted during commit from clone and new object cache so that these are not merged
2842     */

2843    protected void preMergeChanges() {
2844        if (hasObjectsDeletedDuringCommit()) {
2845            for (Enumeration removedObjects = getObjectsDeletedDuringCommit().keys();
2846                     removedObjects.hasMoreElements();) {
2847                Object JavaDoc removedObject = removedObjects.nextElement();
2848                getCloneMapping().remove(removedObject);
2849                getAllClones().remove(removedObject);
2850                // PERF: Avoid initialization of new objects if none.
2851
if (hasNewObjects()) {
2852                    Object JavaDoc referenceObjectToRemove = getNewObjectsCloneToOriginal().get(removedObject);
2853                    if (referenceObjectToRemove != null) {
2854                        getNewObjectsCloneToOriginal().remove(removedObject);
2855                        getNewObjectsOriginalToClone().remove(referenceObjectToRemove);
2856                    }
2857                }
2858            }
2859        }
2860    }
2861
2862    /**
2863     * PUBLIC:
2864     * Print the objects in the unit of work.
2865     * The output of this method will be logged to this unit of work's SessionLog at SEVERE level.
2866     */

2867    public void printRegisteredObjects() {
2868        if (shouldLog(SessionLog.SEVERE, SessionLog.CACHE)) {
2869            basicPrintRegisteredObjects();
2870        }
2871    }
2872
2873    /**
2874     * INTERNAL:
2875     * This method is used to process delete queries that pass through the unitOfWork
2876     * It is extracted out of the internalExecuteQuery method to reduce duplication
2877     */

2878    public Object JavaDoc processDeleteObjectQuery(DeleteObjectQuery deleteQuery) {
2879        // We must ensure that we delete the clone not the original, (this can happen in the mappings update)
2880
if (deleteQuery.getObject() == null) {// Must validate.
2881
throw QueryException.objectToModifyNotSpecified(deleteQuery);
2882        }
2883
2884        ClassDescriptor descriptor = getDescriptor(deleteQuery.getObject());
2885        ObjectBuilder builder = descriptor.getObjectBuilder();
2886        Object JavaDoc implementation = builder.unwrapObject(deleteQuery.getObject(), this);
2887
2888        if (isClassReadOnly(implementation.getClass(), descriptor)) {
2889            throw QueryException.cannotDeleteReadOnlyObject(implementation);
2890        }
2891
2892        if (isCloneNewObject(implementation)) {
2893            unregisterObject(implementation);
2894            return implementation;
2895        }
2896        Vector primaryKey = builder.extractPrimaryKeyFromObject(implementation, this);
2897        Object JavaDoc clone = getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, implementation.getClass(), descriptor, null);
2898        if (clone == null) {
2899            clone = implementation;
2900        }
2901
2902        // Register will wrap so must unwrap again.
2903
clone = builder.unwrapObject(clone, this);
2904
2905        deleteQuery.setObject(clone);
2906        if (!getCommitManager().isActive()) {
2907            getDeletedObjects().put(clone, primaryKey);
2908            return clone;
2909        } else {
2910            // If the object has already been deleted i.e. private-owned + deleted then don't do it twice.
2911
if (hasObjectsDeletedDuringCommit()) {
2912                if (getObjectsDeletedDuringCommit().containsKey(clone)) {
2913                    return clone;
2914                }
2915            }
2916        }
2917        return null;
2918    }
2919
2920    /**
2921     * INTERNAL:
2922     * Print the objects in the unit of work.
2923     */

2924    protected void basicPrintRegisteredObjects() {
2925        String JavaDoc cr = Helper.cr();
2926        StringWriter writer = new StringWriter();
2927        writer.write(LoggingLocalization.buildMessage("unitofwork_identity_hashcode", new Object JavaDoc[] { cr, String.valueOf(System.identityHashCode(this)) }));
2928        if (hasDeletedObjects()) {
2929            writer.write(cr + LoggingLocalization.buildMessage("deleted_objects"));
2930            for (Enumeration enumtr = getDeletedObjects().keys(); enumtr.hasMoreElements();) {
2931                Object JavaDoc object = enumtr.nextElement();
2932                writer.write(LoggingLocalization.buildMessage("key_identity_hash_code_object", new Object JavaDoc[] { cr, Helper.printVector(getDescriptor(object).getObjectBuilder().extractPrimaryKeyFromObject(object, this)), "\t", String.valueOf(System.identityHashCode(object)), object }));
2933            }
2934        }
2935        writer.write(cr + LoggingLocalization.buildMessage("all_registered_clones"));
2936        for (Enumeration enumtr = getCloneMapping().keys(); enumtr.hasMoreElements();) {
2937            Object JavaDoc object = enumtr.nextElement();
2938            writer.write(LoggingLocalization.buildMessage("key_identity_hash_code_object", new Object JavaDoc[] { cr, Helper.printVector(getDescriptor(object).getObjectBuilder().extractPrimaryKeyFromObject(object, this)), "\t", String.valueOf(System.identityHashCode(object)), object }));
2939        }
2940        log(SessionLog.SEVERE, SessionLog.TRANSACTION, writer.toString(), null, null, false);
2941    }
2942
2943    /**
2944     * PUBLIC:
2945     * Register the objects with the unit of work.
2946     * All newly created root domain objects must be registered to be inserted on commit.
2947     * Also any existing objects that will be edited and were not read from this unit of work
2948     * must also be registered.
2949     * Once registered any changes to the objects will be commited to the database on commit.
2950     *
2951     * @return is the clones of the original objects, the return value must be used for editing.
2952     * Editing the original is not allowed in the unit of work.
2953     */

2954    public Vector registerAllObjects(Collection domainObjects) {
2955        Vector clones = new Vector(domainObjects.size());
2956        for (Iterator objectsEnum = domainObjects.iterator(); objectsEnum.hasNext();) {
2957            clones.addElement(registerObject(objectsEnum.next()));
2958        }
2959        return clones;
2960    }
2961
2962    /**
2963     * PUBLIC:
2964     * Register the objects with the unit of work.
2965     * All newly created root domain objects must be registered to be inserted on commit.
2966     * Also any existing objects that will be edited and were not read from this unit of work
2967     * must also be registered.
2968     * Once registered any changes to the objects will be commited to the database on commit.
2969     *
2970     * @return is the clones of the original objects, the return value must be used for editing.
2971     * Editing the original is not allowed in the unit of work.
2972     */

2973    public Vector registerAllObjects(Vector domainObjects) throws DatabaseException, OptimisticLockException {
2974        Vector clones = new Vector(domainObjects.size());
2975        for (Enumeration objectsEnum = domainObjects.elements(); objectsEnum.hasMoreElements();) {
2976            clones.addElement(registerObject(objectsEnum.nextElement()));
2977        }
2978        return clones;
2979    }
2980
2981    /**
2982     * INTERNAL:
2983     * Register the existing object with the unit of work.
2984     * This is a advanced API that can be used if the application can guarentee the object exists on the database.
2985     * When registerObject is called the unit of work determines existence through the descriptor's doesExist setting.
2986     *
2987     * @return The clone of the original object, the return value must be used for editing.
2988     * Editing the original is not allowed in the unit of work.
2989     */

2990    public synchronized Object JavaDoc registerExistingObject(Object JavaDoc existingObject, JoinedAttributeManager joinedAttributeManager) {
2991        if (existingObject == null) {
2992            return null;
2993        }
2994        ClassDescriptor descriptor = getDescriptor(existingObject);
2995        if (descriptor == null) {
2996            throw DescriptorException.missingDescriptor(existingObject.getClass().toString());
2997        }
2998        if (this.isClassReadOnly(descriptor.getJavaClass(), descriptor)) {
2999            return existingObject;
3000        }
3001
3002        ObjectBuilder builder = descriptor.getObjectBuilder();
3003        Object JavaDoc implementation = builder.unwrapObject(existingObject, this);
3004        Object JavaDoc registeredObject = this.registerExistingObject(implementation, descriptor, joinedAttributeManager);
3005
3006        // Bug # 3212057 - workaround JVM bug (MWN)
3007
if (implementation != existingObject) {
3008            return builder.wrapObject(registeredObject, this);
3009        } else {
3010            return registeredObject;
3011        }
3012    }
3013
3014    /**
3015     * ADVANCED:
3016     * Register the existing object with the unit of work.
3017     * This is a advanced API that can be used if the application can guarentee the object exists on the database.
3018     * When registerObject is called the unit of work determines existence through the descriptor's doesExist setting.
3019     *
3020     * @return The clone of the original object, the return value must be used for editing.
3021     * Editing the original is not allowed in the unit of work.
3022     */

3023    public synchronized Object JavaDoc registerExistingObject(Object JavaDoc existingObject) {
3024        return registerExistingObject(existingObject, null);
3025    }
3026
3027    /**
3028     * INTERNAL:
3029     * Register the existing object with the unit of work.
3030     * This is a advanced API that can be used if the application can guarentee the object exists on the database.
3031     * When registerObject is called the unit of work determines existence through the descriptor's doesExist setting.
3032     *
3033     * @return The clone of the original object, the return value must be used for editing.
3034     * Editing the original is not allowed in the unit of work.
3035     */

3036    protected synchronized Object JavaDoc registerExistingObject(Object JavaDoc objectToRegister, ClassDescriptor descriptor, JoinedAttributeManager joinedAttributeManager) {
3037        if (isAfterWriteChangesButBeforeCommit()) {
3038            throw ValidationException.illegalOperationForUnitOfWorkLifecycle(getLifecycle(), "registerExistingObject");
3039        }
3040        if (descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
3041            throw ValidationException.cannotRegisterAggregateObjectInUnitOfWork(objectToRegister.getClass());
3042        }
3043        //CR#2272
3044
logDebugMessage(objectToRegister, "register_existing");
3045        Object JavaDoc registeredObject;
3046        try {
3047            startOperationProfile(SessionProfiler.Register);
3048            registeredObject = checkIfAlreadyRegistered(objectToRegister, descriptor);
3049            if (registeredObject == null) {
3050                // Check if object is existing, if it is it must be cloned into the unit of work
3051
// otherwise it is a new object
3052
Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(objectToRegister, this);
3053
3054                // Always check the cache first.
3055
registeredObject = getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, objectToRegister.getClass(), descriptor, joinedAttributeManager);
3056
3057                if (registeredObject == null) {
3058                    // This is a case where the object is not in the session cache,
3059
// so a new cache-key is used as there is no original to use for locking.
3060
registeredObject = cloneAndRegisterObject(objectToRegister, new CacheKey(primaryKey), joinedAttributeManager);
3061                }
3062            }
3063            
3064            //bug3659327
3065
//fetch group manager control fetch group support
3066
if (descriptor.hasFetchGroupManager()) {
3067                //if the object is already registered in uow, but it's partially fetched (fetch group case)
3068
if (descriptor.getFetchGroupManager().shouldWriteInto(objectToRegister, registeredObject)) {
3069                    //there might be cases when reverting/refreshing clone is needed.
3070
descriptor.getFetchGroupManager().writePartialIntoClones(objectToRegister, registeredObject, this);
3071                }
3072            }
3073        } finally {
3074            endOperationProfile(SessionProfiler.Register);
3075        }
3076        return registeredObject;
3077    }
3078
3079    /**
3080     * ADVANCED:
3081     * Register the new object with the unit of work.
3082     * This will register the new object without cloning.
3083     * Normally the registerObject method should be used for all registration of new and existing objects.
3084     * This version of the register method can only be used for new objects.
3085     * This method should only be used if a new object is desired to be registered without cloning.
3086     *
3087     * @see #registerObject(Object)
3088     */

3089    public synchronized Object JavaDoc registerNewObject(Object JavaDoc newObject) {
3090        if (newObject == null) {
3091            return null;
3092        }
3093        ClassDescriptor descriptor = getDescriptor(newObject);
3094        if (descriptor == null) {
3095            throw DescriptorException.missingDescriptor(newObject.getClass().toString());
3096        }
3097
3098        ObjectBuilder builder = descriptor.getObjectBuilder();
3099        Object JavaDoc implementation = builder.unwrapObject(newObject, this);
3100
3101        this.registerNewObject(implementation, descriptor);
3102
3103        if (implementation == newObject) {
3104            return newObject;
3105        } else {
3106            return builder.wrapObject(implementation, this);
3107        }
3108    }
3109
3110    /**
3111     * INTERNAL:
3112     * Updated to allow passing in of the object's descriptor
3113     *
3114     * Register the new object with the unit of work.
3115     * This will register the new object without cloning.
3116     * Normally the registerObject method should be used for all registration of new and existing objects.
3117     * This version of the register method can only be used for new objects.
3118     * This method should only be used if a new object is desired to be registered without cloning.
3119     *
3120     * @see #registerObject(Object)
3121     */

3122    protected synchronized Object JavaDoc registerNewObject(Object JavaDoc implementation, ClassDescriptor descriptor) {
3123        if (isAfterWriteChangesButBeforeCommit()) {
3124            throw ValidationException.illegalOperationForUnitOfWorkLifecycle(getLifecycle(), "registerNewObject");
3125        }
3126        if (descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
3127            throw ValidationException.cannotRegisterAggregateObjectInUnitOfWork(implementation.getClass());
3128        }
3129        try {
3130            //CR#2272
3131
logDebugMessage(implementation, "register_new");
3132
3133            startOperationProfile(SessionProfiler.Register);
3134            Object JavaDoc registeredObject = checkIfAlreadyRegistered(implementation, descriptor);
3135            if (registeredObject == null) {
3136                // Ensure that the registered object is the one from the parent cache.
3137
if (shouldPerformFullValidation()) {
3138                    Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(implementation, this);
3139                    Object JavaDoc objectFromCache = getParent().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, implementation.getClass(), descriptor, null);
3140                    if (objectFromCache != null) {
3141                        throw ValidationException.wrongObjectRegistered(implementation, objectFromCache);
3142                    }
3143                }
3144                ObjectBuilder builder = descriptor.getObjectBuilder();
3145                Object JavaDoc original = builder.buildNewInstance();
3146
3147                registerNewObjectClone(implementation, original);
3148                Object JavaDoc backupClone = builder.buildNewInstance();
3149                getCloneMapping().put(implementation, backupClone);
3150
3151                // Check if the new objects should be cached.
3152
registerNewObjectInIdentityMap(implementation, implementation);
3153            }
3154        } finally {
3155            endOperationProfile(SessionProfiler.Register);
3156        }
3157
3158        //as this is register new return the object passed in.
3159
return implementation;
3160    }
3161
3162    /**
3163     * INTERNAL:
3164     *
3165     * Register the new object with the unit of work.
3166     * This will register the new object without cloning.
3167     * Checks based on existence will be completed and the create will be cascaded based on the
3168     * object's mappings cascade requirements. This is specific to EJB 3.0 support and is
3169     * @see #registerObject(Object)
3170     */

3171    public synchronized void registerNewObjectForPersist(Object JavaDoc newObject, IdentityHashtable visitedObjects) {
3172        try {
3173            if (newObject == null) {
3174                return;
3175            }
3176            ClassDescriptor descriptor = getDescriptor(newObject);
3177            if ((descriptor == null) || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
3178                throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[]{newObject}));
3179            }
3180            
3181            startOperationProfile(SessionProfiler.Register);
3182            
3183            Object JavaDoc registeredObject = checkIfAlreadyRegistered(newObject, descriptor);
3184            
3185            if (registeredObject == null) {
3186                registerNotRegisteredNewObjectForPersist(newObject, descriptor);
3187            } else if (this.isObjectDeleted(newObject)){
3188                //if object is deleted and a create is issued on the that object
3189
// then the object must be transitioned back to existing and not deleted
3190
this.undeleteObject(newObject);
3191            }
3192            descriptor.getObjectBuilder().cascadeRegisterNewForCreate(newObject, this, visitedObjects);
3193        } finally {
3194            endOperationProfile(SessionProfiler.Register);
3195        }
3196    }
3197
3198    /**
3199     * INTERNAL:
3200     * Called only by registerNewObjectForPersist method,
3201     * and only if newObject is not already registered.
3202     * Could be overridden in subclasses.
3203     */

3204    protected void registerNotRegisteredNewObjectForPersist(Object JavaDoc newObject, ClassDescriptor descriptor) {
3205        // Ensure that the registered object is not detached.
3206
newObject.getClass();
3207
3208        DoesExistQuery existQuery = descriptor.getQueryManager().getDoesExistQuery();
3209        existQuery = (DoesExistQuery)existQuery.clone();
3210        existQuery.setObject(newObject);
3211        existQuery.setDescriptor(descriptor);
3212        // only check the cache as we can wait until commit for the unique
3213
// constraint error to be thrown. This does ignore user's settings
3214
// on descriptor but calling persist() tells us the object is new.
3215
existQuery.checkCacheForDoesExist();
3216        if (((Boolean JavaDoc)executeQuery(existQuery)).booleanValue()) {
3217            throw ValidationException.cannotPersistExistingObject(newObject, this);
3218        }
3219        
3220        logDebugMessage(newObject, "register_new_for_persist");
3221        
3222        if (descriptor.getEventManager().hasAnyEventListeners()) {
3223            oracle.toplink.essentials.descriptors.DescriptorEvent event = new oracle.toplink.essentials.descriptors.DescriptorEvent(newObject);
3224            event.setEventCode(DescriptorEventManager.PrePersistEvent);
3225            event.setSession(this);
3226            descriptor.getEventManager().executeEvent(event);
3227        }
3228        
3229        ObjectBuilder builder = descriptor.getObjectBuilder();
3230        Object JavaDoc original = builder.buildNewInstance();
3231
3232        registerNewObjectClone(newObject, original);
3233        Object JavaDoc backupClone = builder.buildNewInstance();
3234        getCloneMapping().put(newObject, backupClone);
3235        assignSequenceNumber(newObject);
3236
3237        // Check if the new objects should be cached.
3238
registerNewObjectInIdentityMap(newObject, newObject);
3239    }
3240    
3241    /**
3242     * INTERNAL:
3243     * Register the working copy of a new object and its original.
3244     * The user must edit the working copy and the original is used to merge into the parent.
3245     * This mapping is kept both ways because lookup is required in both directions.
3246     */

3247    protected void registerNewObjectClone(Object JavaDoc clone, Object JavaDoc original) {
3248        // Check if the new objects should be cached.
3249
registerNewObjectInIdentityMap(clone, original);
3250
3251        getNewObjectsCloneToOriginal().put(clone, original);
3252        getNewObjectsOriginalToClone().put(original, clone);
3253    }
3254
3255    /**
3256     * INTERNAL:
3257     * Add the new object to the cache if set to.
3258     * This is useful for using mergeclone on new objects.
3259     */

3260    protected void registerNewObjectInIdentityMap(Object JavaDoc clone, Object JavaDoc original) {
3261        // CR 2728 Added check for sequencing to allow zero primitives for id's if the client
3262
//is not using sequencing.
3263
Class JavaDoc cls = clone.getClass();
3264        ClassDescriptor descriptor = getDescriptor(cls);
3265        boolean usesSequences = descriptor.usesSequenceNumbers();
3266        if (shouldNewObjectsBeCached()) {
3267            // Also put it in the cache if it has a valid primary key, this allows for double new object merges
3268
Vector key = keyFromObject(clone, descriptor);
3269            boolean containsNull = false;
3270
3271            // begin CR#2041 Unit Of Work incorrectly put new objects with a primitive primary key in its cache
3272
Object JavaDoc pkElement;
3273            for (int index = 0; index < key.size(); index++) {
3274                pkElement = key.elementAt(index);
3275                if (pkElement == null) {
3276                    containsNull = true;
3277                } else if (usesSequences) {
3278                    containsNull = getSequencing().shouldOverrideExistingValue(cls, pkElement);
3279                }
3280            }
3281            if (containsNull) {
3282                key = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(original, this);
3283                containsNull = false;
3284                for (int index = 0; index < key.size(); index++) {
3285                    pkElement = key.elementAt(index);
3286                    if (pkElement == null) {
3287                        containsNull = true;
3288                    } else if (usesSequences) {
3289                        containsNull = getSequencing().shouldOverrideExistingValue(cls, pkElement);
3290                    }
3291                }
3292            }
3293
3294            // end cr #2041
3295
if (!containsNull) {
3296                getIdentityMapAccessorInstance().putInIdentityMap(clone, key, null, 0, descriptor);
3297            }
3298        }
3299    }
3300
3301    /**
3302     * PUBLIC:
3303     * Register the object with the unit of work.
3304     * All newly created root domain objects must be registered to be inserted on commit.
3305     * Also any existing objects that will be edited and were not read from this unit of work
3306     * must also be registered.
3307     * Once registered any changes to the objects will be commited to the database on commit.
3308     *
3309     * @return the clone of the original object, the return value must be used for editing,
3310     *
3311     * ** Editing the original is not allowed in the unit of work. **
3312     */

3313    public synchronized Object JavaDoc registerObject(Object JavaDoc object) {
3314        if (object == null) {
3315            return null;
3316        }
3317        ClassDescriptor descriptor = getDescriptor(object);
3318        if (descriptor == null) {
3319            throw DescriptorException.missingDescriptor(object.getClass().toString());
3320        }
3321        if (this.isClassReadOnly(descriptor.getJavaClass(), descriptor)) {
3322            return object;
3323        }
3324        ObjectBuilder builder = descriptor.getObjectBuilder();
3325        Object JavaDoc implementation = builder.unwrapObject(object, this);
3326        boolean wasWrapped = implementation != object;
3327        Object JavaDoc registeredObject = this.registerObject(implementation, descriptor);
3328        if (wasWrapped) {
3329            return builder.wrapObject(registeredObject, this);
3330        } else {
3331            return registeredObject;
3332        }
3333    }
3334
3335    /**
3336     * INTERNAL:
3337     * Allows for calling method to provide the descriptor information for this
3338     * object. Prevents double lookup of descriptor.
3339     *
3340     *
3341     * Register the object with the unit of work.
3342     * All newly created root domain objects must be registered to be inserted on commit.
3343     * Also any existing objects that will be edited and were not read from this unit of work
3344     * must also be registered.
3345     * Once registered any changes to the objects will be commited to the database on commit.
3346     *
3347     * calling this method will also sort the objects into different different groups
3348     * depending on if the object being registered is a bean or a regular Java
3349     * object and if its updates are deferred, non-deferred or if all modifications
3350     * are deferred.
3351     *
3352     * @return the clone of the original object, the return value must be used for editing,
3353     */

3354    protected synchronized Object JavaDoc registerObject(Object JavaDoc object, ClassDescriptor descriptor) {
3355        if (this.isClassReadOnly(descriptor.getJavaClass(), descriptor)) {
3356            return object;
3357        }
3358        if (isAfterWriteChangesButBeforeCommit()) {
3359            throw ValidationException.illegalOperationForUnitOfWorkLifecycle(getLifecycle(), "registerObject");
3360        }
3361
3362        //CR#2272
3363
logDebugMessage(object, "register");
3364
3365        Object JavaDoc registeredObject;
3366        try {
3367            startOperationProfile(SessionProfiler.Register);
3368
3369            registeredObject = internalRegisterObject(object, descriptor);
3370
3371        } finally {
3372            endOperationProfile(SessionProfiler.Register);
3373        }
3374        return registeredObject;
3375    }
3376
3377    /**
3378     * INTERNAL:
3379     * Register this UnitOfWork against an external transaction controller
3380     */

3381    public void registerWithTransactionIfRequired() {
3382        if (getParent().hasExternalTransactionController() && ! isSynchronized()) {
3383            boolean hasAlreadyStarted = getParent().wasJTSTransactionInternallyStarted();
3384            getParent().getExternalTransactionController().registerSynchronizationListener(this, getParent());
3385
3386            // CR#2998 - registerSynchronizationListener may toggle the wasJTSTransactionInternallyStarted
3387
// flag. As a result, we must compare the states and if the state is changed, then we must set the
3388
// setWasTransactionBegunPrematurely flag to ensure that we handle the transaction depth count
3389
// appropriately
3390
if (!hasAlreadyStarted && getParent().wasJTSTransactionInternallyStarted()) {
3391                // registerSynchronizationListener caused beginTransaction() called
3392
// and an external transaction internally started.
3393
this.setWasTransactionBegunPrematurely(true);
3394            }
3395        }
3396    }
3397
3398    /**
3399     * PUBLIC:
3400     * Release the unit of work. This terminates this unit of work.
3401     * Because the unit of work operates on its own object space (clones) no work is required.
3402     * The unit of work should no longer be used or referenced by the application beyond this point
3403     * so that it can be garbage collected.
3404     *
3405     * @see #commit()
3406     */

3407    public void release() {
3408        log(SessionLog.FINER, SessionLog.TRANSACTION, "release_unit_of_work");
3409        getEventManager().preReleaseUnitOfWork();
3410
3411        // If already succeeded at a writeChanges(), then transaction still open.
3412
// As already issued sql must at least mark the external transaction for rollback only.
3413
if (getLifecycle() == CommitTransactionPending) {
3414            if (hasModifications() || wasTransactionBegunPrematurely()) {
3415                rollbackTransaction(false);
3416                setWasTransactionBegunPrematurely(false);
3417            }
3418        } else if (wasTransactionBegunPrematurely() && (!isNestedUnitOfWork())) {
3419            rollbackTransaction();
3420            setWasTransactionBegunPrematurely(false);
3421        }
3422        if ((getMergeManager() != null) && (getMergeManager().getAcquiredLocks() != null) && (!getMergeManager().getAcquiredLocks().isEmpty())) {
3423            //may have unreleased cache locks because of a rollback... As some
3424
//locks may be acquired durring commit.
3425
getParent().getIdentityMapAccessorInstance().getWriteLockManager().releaseAllAcquiredLocks(getMergeManager());
3426            this.setMergeManager(null);
3427        }
3428        setDead();
3429        getParent().releaseUnitOfWork(this);
3430        getEventManager().postReleaseUnitOfWork();
3431    }
3432
3433    /**
3434     * PUBLIC:
3435     * Empties the set of read-only classes.
3436     * It is illegal to call this method on nested UnitOfWork objects. A nested UnitOfWork
3437     * cannot have a subset of its parent's set of read-only classes.
3438     * Also removes classes which are read only because their descriptors are readonly
3439     */

3440    public void removeAllReadOnlyClasses() throws ValidationException {
3441        if (isNestedUnitOfWork()) {
3442            throw ValidationException.cannotRemoveFromReadOnlyClassesInNestedUnitOfWork();
3443        }
3444        getReadOnlyClasses().clear();
3445    }
3446
3447    /**
3448     * ADVANCED:
3449     * Remove optimistic read lock from the object
3450     * See forceUpdateToVersionField(Object)
3451     */

3452    public void removeForceUpdateToVersionField(Object JavaDoc lockObject) {
3453        getOptimisticReadLockObjects().remove(lockObject);
3454    }
3455
3456    /**
3457     * PUBLIC:
3458     * Removes a Class from the receiver's set of read-only classes.
3459     * It is illegal to try to send this method to a nested UnitOfWork.
3460     */

3461    public void removeReadOnlyClass(Class JavaDoc theClass) throws ValidationException {
3462        if (!canChangeReadOnlySet()) {
3463            throw ValidationException.cannotModifyReadOnlyClassesSetAfterUsingUnitOfWork();
3464        }
3465        if (isNestedUnitOfWork()) {
3466            throw ValidationException.cannotRemoveFromReadOnlyClassesInNestedUnitOfWork();
3467        }
3468        getReadOnlyClasses().remove(theClass);
3469
3470    }
3471
3472    /**
3473     * INTERNAL:
3474     * Used in the resume to reset the all clones collection
3475     */

3476    protected void resetAllCloneCollection() {
3477        this.allClones = null;
3478    }
3479
3480    /**
3481     * PUBLIC:
3482     * Revert all changes made to any registered object.
3483     * Clear all deleted and new objects.
3484     * Revert should not be confused with release which it the normal compliment to commit.
3485     * Revert is more similar to commit and resume, however reverts all changes and resumes.
3486     * If you do not require to resume the unit of work release should be used instead.
3487     *
3488     * @see #commitAndResume()
3489     * @see #release()
3490     */

3491    public void revertAndResume() {
3492        if (isAfterWriteChangesButBeforeCommit()) {
3493            throw ValidationException.illegalOperationForUnitOfWorkLifecycle(getLifecycle(), "revertAndResume");
3494        }
3495        log(SessionLog.FINER, SessionLog.TRANSACTION, "revert_unit_of_work");
3496
3497        MergeManager manager = new MergeManager(this);
3498        manager.mergeOriginalIntoWorkingCopy();
3499        manager.cascadeAllParts();
3500        for (Enumeration cloneEnum = getCloneMapping().keys(); cloneEnum.hasMoreElements();) {
3501            Object JavaDoc clone = cloneEnum.nextElement();
3502
3503            // Revert each clone.
3504
manager.mergeChanges(clone, null);
3505            ClassDescriptor descriptor = this.getDescriptor(clone);
3506
3507            //revert the tracking policy
3508
descriptor.getObjectChangePolicy().revertChanges(clone, descriptor, this, this.getCloneMapping());
3509        }
3510
3511        // PERF: Avoid initialization of new objects if none.
3512
if (hasNewObjects()) {
3513            for (Enumeration cloneEnum = getNewObjectsCloneToOriginal().keys();
3514                     cloneEnum.hasMoreElements();) {
3515                Object JavaDoc clone = cloneEnum.nextElement();
3516
3517                // De-register the object.
3518
getCloneMapping().remove(clone);
3519                
3520            }
3521            if (this.getUnitOfWorkChangeSet() != null){
3522                ((UnitOfWorkChangeSet)this.getUnitOfWorkChangeSet()).getNewObjectChangeSets().clear();
3523            }
3524        }
3525
3526        // Clear new and deleted objects.
3527
setNewObjectsCloneToOriginal(null);
3528        setNewObjectsOriginalToClone(null);
3529        // Reset the all clones collection
3530
resetAllCloneCollection();
3531        // 2612538 - the default size of IdentityHashtable (32) is appropriate
3532
setObjectsDeletedDuringCommit(new IdentityHashtable());
3533        setDeletedObjects(new IdentityHashtable());
3534        setRemovedObjects(new IdentityHashtable());
3535        setUnregisteredNewObjects(new IdentityHashtable());
3536        log(SessionLog.FINER, SessionLog.TRANSACTION, "resume_unit_of_work");
3537    }
3538
3539    /**
3540     * PUBLIC:
3541     * Revert the object's attributes from the parent.
3542     * This also reverts the object privately-owned parts.
3543     *
3544     * @return the object reverted.
3545     * @see #shallowRevertObject(Object)
3546     * @see #deepRevertObject(Object)
3547     */

3548    public Object JavaDoc revertObject(Object JavaDoc clone) {
3549        return revertObject(clone, MergeManager.CASCADE_PRIVATE_PARTS);
3550    }
3551
3552    /**
3553     * INTERNAL:
3554     * Revert the object's attributes from the parent.
3555     * This uses merging to merge the object changes.
3556     */

3557    public Object JavaDoc revertObject(Object JavaDoc clone, int cascadeDepth) {
3558        if (clone == null) {
3559            return null;
3560        }
3561
3562        //CR#2272
3563
logDebugMessage(clone, "revert");
3564
3565        ClassDescriptor descriptor = getDescriptor(clone);
3566        ObjectBuilder builder = descriptor.getObjectBuilder();
3567        Object JavaDoc implementation = builder.unwrapObject(clone, this);
3568
3569        MergeManager manager = new MergeManager(this);
3570        manager.mergeOriginalIntoWorkingCopy();
3571        manager.setCascadePolicy(cascadeDepth);
3572        try {
3573            manager.mergeChanges(implementation, null);
3574        } catch (RuntimeException JavaDoc exception) {
3575            return handleException(exception);
3576        }
3577        return clone;
3578    }
3579
3580    /**
3581     * INTERNAL:
3582     * This is internal to the uow, transactions should not be used explictly in a uow.
3583     * The uow shares its parents transactions.
3584     */

3585    public void rollbackTransaction() throws DatabaseException {
3586        incrementProfile(SessionProfiler.UowRollbacks);
3587        getParent().rollbackTransaction();
3588    }
3589
3590    /**
3591     * INTERNAL:
3592     * rollbackTransaction() with a twist for external transactions.
3593     * <p>
3594     * writeChanges() is called outside the JTA beforeCompletion(), so the
3595     * accompanying exception won't propogate up and cause a rollback by itself.
3596     * <p>
3597     * Instead must mark the transaction for rollback only here.
3598     * <p>
3599     * If internally started external transaction or no external transaction
3600     * can still rollback normally.
3601     * @param intendedToCommitTransaction whether we were inside a commit or just trying to
3602     * write out changes early.
3603     */

3604    protected void rollbackTransaction(boolean intendedToCommitTransaction) throws DatabaseException {
3605        if (!intendedToCommitTransaction && getParent().hasExternalTransactionController() && !getParent().wasJTSTransactionInternallyStarted()) {
3606            getParent().getExternalTransactionController().markTransactionForRollback();
3607        }
3608        rollbackTransaction();
3609    }
3610
3611    /**
3612     * INTERNAL:
3613     * Scans the UnitOfWork identity map for conforming instances.
3614     * <p>
3615     * Later this method can be made recursive to check all parent units of
3616     * work also.
3617     * @param selectionCriteria must be cloned and specially prepared for conforming
3618     * @return IdentityHashtable to facilitate merging with conforming instances
3619     * returned from a query on the database.
3620     */

3621    public IdentityHashtable scanForConformingInstances(Expression selectionCriteria, Class JavaDoc referenceClass, AbstractRecord arguments, ObjectLevelReadQuery query) {
3622        // for bug 3568141 use the painstaking shouldTriggerIndirection if set
3623
InMemoryQueryIndirectionPolicy policy = query.getInMemoryQueryIndirectionPolicy();
3624        if (!policy.shouldTriggerIndirection()) {
3625            policy = new InMemoryQueryIndirectionPolicy(InMemoryQueryIndirectionPolicy.SHOULD_IGNORE_EXCEPTION_RETURN_NOT_CONFORMED);
3626        }
3627        IdentityHashtable indexedInterimResult = new IdentityHashtable();
3628        try {
3629            Vector fromCache = null;
3630            if (selectionCriteria != null) {
3631                // assume objects that have the compared relationship
3632
// untriggered do not conform as they have not been changed.
3633
// bug 2637555
3634
fromCache = getIdentityMapAccessor().getAllFromIdentityMap(selectionCriteria, referenceClass, arguments, policy);
3635                for (Enumeration fromCacheEnum = fromCache.elements();
3636                         fromCacheEnum.hasMoreElements();) {
3637                    Object JavaDoc object = fromCacheEnum.nextElement();
3638                    if (!isObjectDeleted(object)) {
3639                        indexedInterimResult.put(object, object);
3640                    }
3641                }
3642            }
3643
3644            // Add any new objects that conform to the query.
3645
Vector newObjects = null;
3646            newObjects = getAllFromNewObjects(selectionCriteria, referenceClass, arguments, policy);
3647            for (Enumeration newObjectsEnum = newObjects.elements();
3648                     newObjectsEnum.hasMoreElements();) {
3649                Object JavaDoc object = newObjectsEnum.nextElement();
3650                if (!isObjectDeleted(object)) {
3651                    indexedInterimResult.put(object, object);
3652                }
3653            }
3654        } catch (QueryException exception) {
3655            if (getShouldThrowConformExceptions() == THROW_ALL_CONFORM_EXCEPTIONS) {
3656                throw exception;
3657            }
3658        }
3659        return indexedInterimResult;
3660    }
3661
3662    /**
3663     * INTERNAL:
3664     * Used to set the collections of all objects in the UnitOfWork.
3665     * @param newUnregisteredExistingObjects oracle.toplink.essentials.internal.helper.IdentityHashtable
3666     */

3667    protected void setAllClonesCollection(IdentityHashtable objects) {
3668        this.allClones = objects;
3669    }
3670
3671    /**
3672     * INTERNAL:
3673     * Set the clone mapping.
3674     * The clone mapping contains clone of all registered objects,
3675     * this is required to store the original state of the objects when registered
3676
3677     * so that only what is changed will be commited to the database and the parent,
3678     * (this is required to support parralel unit of work).
3679     */

3680    protected void setCloneMapping(IdentityHashtable cloneMapping) {
3681        this.cloneMapping = cloneMapping;
3682    }
3683
3684    /**
3685     * INTERNAL:
3686     * set UoW lifecycle state variable to DEATH
3687     */

3688    public void setDead() {
3689        setLifecycle(Death);
3690    }
3691
3692    /**
3693     * INTERNAL:
3694     * The deleted objects stores any objects removed during the unit of work.
3695     * On commit they will all be removed from the database.
3696     */

3697    protected void setDeletedObjects(IdentityHashtable deletedObjects) {
3698        this.deletedObjects = deletedObjects;
3699    }
3700
3701    /**
3702     * INTERNAL:
3703     * The life cycle tracks if the unit of work is active and is used for JTS.
3704     */

3705    protected void setLifecycle(int lifecycle) {
3706        this.lifecycle = lifecycle;
3707    }
3708
3709    /**
3710     * INTERNAL:
3711     * A reference to the last used merge manager. This is used to track locked
3712     * objects.
3713     */

3714    public void setMergeManager(MergeManager mergeManager) {
3715        this.lastUsedMergeManager = mergeManager;
3716    }
3717
3718    /**
3719     * INTERNAL:
3720     * The new objects stores any objects newly created during the unit of work.
3721     * On commit they will all be inserted into the database.
3722     */

3723    protected void setNewObjectsCloneToOriginal(IdentityHashtable newObjects) {
3724        this.newObjectsCloneToOriginal = newObjects;
3725    }
3726
3727    /**
3728     * INTERNAL:
3729     * The new objects stores any objects newly created during the unit of work.
3730     * On commit they will all be inserted into the database.
3731     */

3732    protected void setNewObjectsOriginalToClone(IdentityHashtable newObjects) {
3733        this.newObjectsOriginalToClone = newObjects;
3734    }
3735
3736    /**
3737     * INTERNAL:
3738     * Set the objects that have been deleted.
3739     */

3740    public void setObjectsDeletedDuringCommit(IdentityHashtable deletedObjects) {
3741        objectsDeletedDuringCommit = deletedObjects;
3742    }
3743
3744    /**
3745     * INTERNAL:
3746     * Set the parent.
3747     * This is a unit of work if nested, otherwise a database session or client session.
3748     */

3749    public void setParent(AbstractSession parent) {
3750        this.parent = parent;
3751    }
3752
3753    /**
3754     * INTERNAL:
3755     * set UoW lifecycle state variable to PENDING_MERGE
3756     */

3757    public void setPendingMerge() {
3758        setLifecycle(MergePending);
3759    }
3760
3761    /**
3762     * INTERNAL:
3763     * Gives a new set of read-only classes to the receiver.
3764     * This set of classes given are checked that subclasses of a read-only class are also
3765     * in the read-only set provided.
3766     */

3767    public void setReadOnlyClasses(Vector classes) {
3768        this.readOnlyClasses = new Hashtable(classes.size() + 10);
3769        for (Enumeration enumtr = classes.elements(); enumtr.hasMoreElements();) {
3770            Class JavaDoc theClass = (Class JavaDoc)enumtr.nextElement();
3771            addReadOnlyClass(theClass);
3772        }
3773    }
3774
3775    /**
3776     * INTERNAL:
3777     * The removed objects stores any newly registered objects removed during the nested unit of work.
3778     * On commit they will all be removed from the parent unit of work.
3779     */

3780    protected void setRemovedObjects(IdentityHashtable removedObjects) {
3781        this.removedObjects = removedObjects;
3782    }
3783
3784    /**
3785     * INTERNAL:
3786     * Set if this UnitofWork should be resumed after the end of the transaction
3787     * Used when UnitOfWork is synchronized with external transaction control
3788     */

3789    public void setResumeUnitOfWorkOnTransactionCompletion(boolean resumeUnitOfWork){
3790        this.resumeOnTransactionCompletion = resumeUnitOfWork;
3791    }
3792
3793    /**
3794     * INTERNAL:
3795     * True if the value holder for the joined attribute should be triggered.
3796     * Required by ejb30 fetch join.
3797     */

3798    public void setShouldCascadeCloneToJoinedRelationship(boolean shouldCascadeCloneToJoinedRelationship) {
3799        this.shouldCascadeCloneToJoinedRelationship = shouldCascadeCloneToJoinedRelationship;
3800    }
3801
3802    /**
3803     * ADVANCED:
3804     * By default new objects are not cached until the exist on the database.
3805     * Occasionally if mergeClone is used on new objects and is required to allow multiple merges
3806     * on the same new object, then if the new objects are not cached, each mergeClone will be
3807     * interpretted as a different new object.
3808     * By setting new objects to be cached mergeClone can be performed multiple times before commit.
3809     * New objects cannot be cached unless they have a valid assigned primary key before being registered.
3810     * New object with non-null invalid primary keys such as 0 or '' can cause problems and should not be used with this option.
3811     */

3812    public void setShouldNewObjectsBeCached(boolean shouldNewObjectsBeCached) {
3813        this.shouldNewObjectsBeCached = shouldNewObjectsBeCached;
3814    }
3815
3816    /**
3817     * ADVANCED:
3818     * By default deletes are performed last in a unit of work.
3819     * Sometimes you may want to have the deletes performed before other actions.
3820     */

3821    public void setShouldPerformDeletesFirst(boolean shouldPerformDeletesFirst) {
3822        this.shouldPerformDeletesFirst = shouldPerformDeletesFirst;
3823    }
3824
3825    /**
3826     * ADVANCED:
3827     * Conforming queries can be set to provide different levels of detail about the
3828     * exceptions they encounter
3829     * There are three levels:
3830     * DO_NOT_THROW_CONFORM_EXCEPTIONS = 0;
3831     * THROW_ALL_CONFORM_EXCEPTIONS = 1;
3832     */

3833    public void setShouldThrowConformExceptions(int shouldThrowExceptions) {
3834        this.shouldThrowConformExceptions = shouldThrowExceptions;
3835    }
3836
3837    /**
3838     * INTERNAL:
3839     * Set smart merge flag. This feature is used in WL to merge dependent values without SessionAccessor
3840     */

3841    public static void setSmartMerge(boolean option) {
3842        SmartMerge = option;
3843    }
3844
3845    /**
3846     * INTERNAL:
3847     * Set isSynchronized flag to indicate that this session is a synchronized unit of work.
3848     */

3849    public void setSynchronized(boolean synched) {
3850        isSynchronized = synched;
3851    }
3852
3853    /**
3854     * INTERNAL:
3855     * Sets the current UnitOfWork change set to be the one passed in.
3856     */

3857    public void setUnitOfWorkChangeSet(UnitOfWorkChangeSet unitOfWorkChangeSet) {
3858        this.unitOfWorkChangeSet = unitOfWorkChangeSet;
3859    }
3860
3861    /**
3862     * INTERNAL:
3863     * Used to set the unregistered existing objects vector used when validation has been turned off.
3864     * @param newUnregisteredExistingObjects oracle.toplink.essentials.internal.helper.IdentityHashtable
3865     */

3866    protected void setUnregisteredExistingObjects(oracle.toplink.essentials.internal.helper.IdentityHashtable newUnregisteredExistingObjects) {
3867        unregisteredExistingObjects = newUnregisteredExistingObjects;
3868    }
3869
3870    /**
3871     * INTERNAL:
3872     */

3873    protected void setUnregisteredNewObjects(IdentityHashtable newObjects) {
3874        unregisteredNewObjects = newObjects;
3875    }
3876
3877    /**
3878     * ADVANCED:
3879     * The unit of work performs validations such as,
3880     * ensuring multiple copies of the same object don't exist in the same unit of work,
3881     * ensuring deleted objects are not refered after commit,
3882     * ensures that objects from the parent cache are not refered in the unit of work cache.
3883     * The level of validation can be increased or decreased for debugging purposes or under
3884     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
3885     * It is strongly suggested that clone identity not be violate in the unit of work.
3886     */

3887    public void setValidationLevel(int validationLevel) {
3888        this.validationLevel = validationLevel;
3889    }
3890
3891    /**
3892     * INTERNAL:
3893     * Set a flag in the root UOW to indicate that a pess. locking or non-selecting SQL query was executed
3894     * and forced a transaction to be started.
3895     */

3896    public void setWasTransactionBegunPrematurely(boolean wasTransactionBegunPrematurely) {
3897        if (isNestedUnitOfWork()) {
3898            ((UnitOfWorkImpl)getParent()).setWasTransactionBegunPrematurely(wasTransactionBegunPrematurely);
3899        }
3900        this.wasTransactionBegunPrematurely = wasTransactionBegunPrematurely;
3901    }
3902
3903    /**
3904     * PUBLIC:
3905     * Merge the attributes of the clone into the unit of work copy.
3906     * This can be used for objects that are returned from the client through
3907     * RMI serialization (or other serialization mechanisms), because the RMI object will
3908     * be a clone this will merge its attributes correctly to preserve object identity
3909     * within the unit of work and record its changes.
3910     *
3911     * Only direct attributes are merged.
3912     *
3913     * @return the registered version for the clone being merged.
3914     * @see #mergeClone(Object)
3915     * @see #deepMergeClone(Object)
3916     */

3917    public Object JavaDoc shallowMergeClone(Object JavaDoc rmiClone) {
3918        return mergeClone(rmiClone, MergeManager.NO_CASCADE);
3919    }
3920
3921    /**
3922     * PUBLIC:
3923     * Revert the object's attributes from the parent.
3924     * This only reverts the object's direct attributes.
3925     *
3926     * @return the object reverted.
3927     * @see #revertObject(Object)
3928     * @see #deepRevertObject(Object)
3929     */

3930    public Object JavaDoc shallowRevertObject(Object JavaDoc clone) {
3931        return revertObject(clone, MergeManager.NO_CASCADE);
3932    }
3933
3934    /**
3935     * ADVANCED:
3936     * Unregister the object with the unit of work.
3937     * This can be used to delete an object that was just created and is not yet persistent.
3938     * Delete object can also be used, but will result in inserting the object and then deleting it.
3939     * The method will only unregister the clone, none of its parts.
3940     */

3941    public void shallowUnregisterObject(Object JavaDoc clone) {
3942        unregisterObject(clone, DescriptorIterator.NoCascading);
3943    }
3944
3945    /**
3946     * INTERNAL:
3947     * True if the value holder for the joined attribute should be triggered.
3948     * Required by ejb30 fetch join.
3949     */

3950    public boolean shouldCascadeCloneToJoinedRelationship() {
3951        return shouldCascadeCloneToJoinedRelationship;
3952    }
3953
3954    /**
3955     * ADVANCED:
3956     * By default new objects are not cached until the exist on the database.
3957     * Occasionally if mergeClone is used on new objects and is required to allow multiple merges
3958     * on the same new object, then if the new objects are not cached, each mergeClone will be
3959     * interpretted as a different new object.
3960     * By setting new objects to be cached mergeClone can be performed multiple times before commit.
3961     * New objects cannot be cached unless they have a valid assigned primary key before being registered.
3962     * New object with non-null invalid primary keys such as 0 or '' can cause problems and should not be used with this option.
3963     */

3964    public boolean shouldNewObjectsBeCached() {
3965        return shouldNewObjectsBeCached;
3966    }
3967
3968    /**
3969     * ADVANCED:
3970     * By default all objects are inserted and updated in the database before
3971     * any object is deleted. If this flag is set to true, deletes will be
3972     * performed before inserts and updates
3973     */

3974    public boolean shouldPerformDeletesFirst() {
3975        return shouldPerformDeletesFirst;
3976    }
3977
3978    /**
3979     * ADVANCED:
3980     * The unit of work performs validations such as,
3981     * ensuring multiple copies of the same object don't exist in the same unit of work,
3982     * ensuring deleted objects are not refered after commit,
3983     * ensures that objects from the parent cache are not refered in the unit of work cache.
3984     * The level of validation can be increased or decreased for debugging purposes or under
3985     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
3986     * It is strongly suggested that clone identity not be violate in the unit of work.
3987     */

3988    public boolean shouldPerformFullValidation() {
3989        return getValidationLevel() == Full;
3990    }
3991
3992    /**
3993     * ADVANCED:
3994     * The unit of work performs validations such as,
3995     * ensuring multiple copies of the same object don't exist in the same unit of work,
3996     * ensuring deleted objects are not refered after commit,
3997     * ensures that objects from the parent cache are not refered in the unit of work cache.
3998     * The level of validation can be increased or decreased for debugging purposes or under
3999     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
4000     * It is strongly suggested that clone identity not be violate in the unit of work.
4001     */

4002    public boolean shouldPerformNoValidation() {
4003        return getValidationLevel() == None;
4004    }
4005
4006    /**
4007     * ADVANCED:
4008     * The unit of work performs validations such as,
4009     * ensuring multiple copies of the same object don't exist in the same unit of work,
4010     * ensuring deleted objects are not refered after commit,
4011     * ensures that objects from the parent cache are not refered in the unit of work cache.
4012     * The level of validation can be increased or decreased for debugging purposes or under
4013     * advanced situation where the application requires/desires to violate clone identity in the unit of work.
4014     * It is strongly suggested that clone identity not be violate in the unit of work.
4015     */

4016    public boolean shouldPerformPartialValidation() {
4017        return getValidationLevel() == Partial;
4018    }
4019
4020    /**
4021     * INTERNAL:
4022     * Returns true if this UnitofWork should be resumed after the end of the transaction
4023     * Used when UnitOfWork is synchronized with external transaction control
4024     */

4025    public boolean shouldResumeUnitOfWorkOnTransactionCompletion(){
4026        return this.resumeOnTransactionCompletion;
4027    }
4028    
4029    /**
4030     * INTERNAL:
4031     * Store the ModifyAllQuery's from the UoW in the list. They are always
4032     * deferred to commit time
4033     */

4034    public void storeModifyAllQuery(DatabaseQuery query) {
4035        if (modifyAllQueries == null) {
4036            modifyAllQueries = new ArrayList();
4037        }
4038
4039        modifyAllQueries.add(query);
4040    }
4041
4042    /**
4043     * INTERNAL:
4044     * Store the deferred UpdateAllQuery's from the UoW in the list.
4045     */

4046    public void storeDeferredModifyAllQuery(DatabaseQuery query, AbstractRecord translationRow) {
4047        if (deferredModifyAllQueries == null) {
4048            deferredModifyAllQueries = new ArrayList();
4049        }
4050        deferredModifyAllQueries.add(new Object JavaDoc[]{query, translationRow});
4051    }
4052
4053    /**
4054     * INTERNAL
4055     * Synchronize the clones and update their backup copies.
4056     * Called after commit and commit and resume.
4057     */

4058    public void synchronizeAndResume() {
4059        // For pessimistic locking all locks were released by commit.
4060
getPessimisticLockedObjects().clear();
4061        getProperties().remove(LOCK_QUERIES_PROPERTY);
4062
4063        // find next power-of-2 size
4064
IdentityHashtable newCloneMapping = new IdentityHashtable(1 + getCloneMapping().size());
4065
4066        for (Enumeration cloneEnum = getCloneMapping().keys(); cloneEnum.hasMoreElements();) {
4067            Object JavaDoc clone = cloneEnum.nextElement();
4068
4069            // Do not add object that were deleted, what about private parts??
4070
if ((!isObjectDeleted(clone)) && (!getRemovedObjects().containsKey(clone))) {
4071                ClassDescriptor descriptor = getDescriptor(clone);
4072                ObjectBuilder builder = descriptor.getObjectBuilder();
4073
4074                //Build backup clone for DeferredChangeDetectionPolicy or ObjectChangeTrackingPolicy,
4075
//but not for AttributeChangeTrackingPolicy
4076
descriptor.getObjectChangePolicy().revertChanges(clone, descriptor, this, newCloneMapping);
4077            }
4078        }
4079        setCloneMapping(newCloneMapping);
4080
4081        if (hasObjectsDeletedDuringCommit()) {
4082            for (Enumeration removedObjects = getObjectsDeletedDuringCommit().keys();
4083                     removedObjects.hasMoreElements();) {
4084                Object JavaDoc removedObject = removedObjects.nextElement();
4085                getIdentityMapAccessor().removeFromIdentityMap((Vector)getObjectsDeletedDuringCommit().get(removedObject), removedObject.getClass());
4086            }
4087        }
4088
4089        // New objects are not new anymore.
4090
// can not set multi clone for NestedUnitOfWork.CR#2015 - XC
4091
if (!isNestedUnitOfWork()) {
4092            //Need to move objects and clones from NewObjectsCloneToOriginal to CloneToOriginals for use in the continued uow
4093
if (hasNewObjects()) {
4094                for (Enumeration newClones = getNewObjectsCloneToOriginal().keys(); newClones.hasMoreElements();) {
4095                    Object JavaDoc newClone = newClones.nextElement();
4096                    getCloneToOriginals().put(newClone, getNewObjectsCloneToOriginal().get(newClone));
4097                }
4098            }
4099            setNewObjectsCloneToOriginal(null);
4100            setNewObjectsOriginalToClone(null);
4101        }
4102
4103        //reset unitOfWorkChangeSet. Needed for ObjectChangeTrackingPolicy and DeferredChangeDetectionPolicy
4104
setUnitOfWorkChangeSet(null);
4105
4106        // The collections of clones may change in the new UnitOfWork
4107
resetAllCloneCollection();
4108        // 2612538 - the default size of IdentityHashtable (32) is appropriate
4109
setObjectsDeletedDuringCommit(new IdentityHashtable());
4110        setDeletedObjects(new IdentityHashtable());
4111        setRemovedObjects(new IdentityHashtable());
4112        setUnregisteredNewObjects(new IdentityHashtable());
4113        //Reset lifecycle
4114
this.lifecycle = Birth;
4115        this.isSynchronized = false;
4116    }
4117
4118    /**
4119     * INTERNAL:
4120     * THis method is used to transition an object from the deleted objects list
4121     * to be simply be register.
4122     */

4123    protected void undeleteObject(Object JavaDoc object){
4124        getDeletedObjects().remove(object);
4125        if (getParent().isUnitOfWork()) {
4126            ((UnitOfWorkImpl)getParent()).undeleteObject(object);
4127        }
4128    }
4129    
4130    /**
4131     * PUBLIC:
4132     * Unregister the object with the unit of work.
4133     * This can be used to delete an object that was just created and is not yet persistent.
4134     * Delete object can also be used, but will result in inserting the object and then deleting it.
4135     * The method will only unregister the object and its privately owned parts
4136     */

4137    public void unregisterObject(Object JavaDoc clone) {
4138        unregisterObject(clone, DescriptorIterator.CascadePrivateParts);
4139    }
4140
4141    /**
4142     * INTERNAL:
4143     * Unregister the object with the unit of work.
4144     * This can be used to delete an object that was just created and is not yet persistent.
4145     * Delete object can also be used, but will result in inserting the object and then deleting it.
4146     */

4147    public void unregisterObject(Object JavaDoc clone, int cascadeDepth) {
4148        // Allow register to be called with null and just return true
4149
if (clone == null) {
4150            return;
4151        }
4152
4153        //CR#2272
4154
logDebugMessage(clone, "unregister");
4155        Object JavaDoc implementation = getDescriptor(clone).getObjectBuilder().unwrapObject(clone, this);
4156
4157        // This define an inner class for process the itteration operation, don't be scared, its just an inner class.
4158
DescriptorIterator iterator = new DescriptorIterator() {
4159            public void iterate(Object JavaDoc object) {
4160                if (isClassReadOnly(object.getClass(), getCurrentDescriptor())) {
4161                    setShouldBreak(true);
4162                    return;
4163                }
4164
4165                // Check if object exists in the IM.
4166
Vector primaryKey = getCurrentDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(object, UnitOfWorkImpl.this);
4167
4168                // If object exists in IM remove it from the IM and also from clone mapping.
4169
getIdentityMapAccessorInstance().removeFromIdentityMap(primaryKey, object.getClass(), getCurrentDescriptor());
4170                getCloneMapping().remove(object);
4171
4172                // Remove object from the new object cache
4173
// PERF: Avoid initialization of new objects if none.
4174
if (hasNewObjects()) {
4175                    Object JavaDoc original = getNewObjectsCloneToOriginal().remove(object);
4176                    if (original != null) {
4177                        getNewObjectsOriginalToClone().remove(original);
4178                    }
4179                }
4180            }
4181        };
4182
4183        iterator.setSession(this);
4184        iterator.setCascadeDepth(cascadeDepth);
4185        iterator.startIterationOn(implementation);
4186    }
4187
4188    /**
4189     * INTERNAL:
4190     * This method is used internally to update the tracked objects if required
4191     */

4192    public void updateChangeTrackersIfRequired(Object JavaDoc objectToWrite, ObjectChangeSet changeSetToWrite, UnitOfWorkImpl uow, ClassDescriptor descriptor) {
4193        //this is a no op in this unitOfWork Class see subclasses for implementation.
4194
}
4195
4196    /**
4197     * ADVANCED:
4198     * This can be used to help debugging an object-space corruption.
4199     * An object-space corruption is when your application has incorrectly related a clone to an original object.
4200     * This method will validate that all registered objects are in a correct state and throw
4201     * an error if not, it will contain the full stack of object references in the error message.
4202     * If you call this method after each register or change you perform it will pin-point where the error was made.
4203     */

4204    public void validateObjectSpace() {
4205        log(SessionLog.FINER, SessionLog.TRANSACTION, "validate_object_space");
4206        // This define an inner class for process the itteration operation, don't be scared, its just an inner class.
4207
DescriptorIterator iterator = new DescriptorIterator() {
4208            public void iterate(Object JavaDoc object) {
4209                try {
4210                    if (isClassReadOnly(object.getClass(), getCurrentDescriptor())) {
4211                        setShouldBreak(true);
4212                        return;
4213                    } else {
4214                        getBackupClone(object);
4215                    }
4216                } catch (TopLinkException exception) {
4217                    log(SessionLog.FINEST, SessionLog.TRANSACTION, "stack_of_visited_objects_that_refer_to_the_corrupt_object", getVisitedStack());
4218                    log(SessionLog.FINER, SessionLog.TRANSACTION, "corrupt_object_referenced_through_mapping", getCurrentMapping());
4219                    throw exception;
4220                }
4221            }
4222        };
4223
4224        iterator.setSession(this);
4225        for (Enumeration clonesEnum = getCloneMapping().keys(); clonesEnum.hasMoreElements();) {
4226            iterator.startIterationOn(clonesEnum.nextElement());
4227        }
4228    }
4229
4230    /**
4231     * INTERNAL:
4232     * Indicates if a transaction was begun by a pessimistic locking or non-selecting query.
4233     * Traverse to the root UOW to get value.
4234     */

4235
4236    // * 2.5.1.8 Nov 17, 2000 JED
4237
// * Prs 25751 Changed to make this method public
4238
public boolean wasTransactionBegunPrematurely() {
4239        if (isNestedUnitOfWork()) {
4240            return ((UnitOfWorkImpl)getParent()).wasTransactionBegunPrematurely();
4241        }
4242        return wasTransactionBegunPrematurely;
4243    }
4244
4245    /**
4246     * ADVANCED: Writes all changes now before commit().
4247     * The commit process will begin and all changes will be written out to the datastore, but the datastore transaction will not
4248     * be committed, nor will changes be merged into the global cache.
4249     * <p>
4250     * A subsequent commit (on UnitOfWork or global transaction) will be required to finalize the commit process.
4251     * <p>
4252     * As the commit process has begun any attempt to register objects, or execute object-level queries will
4253     * generate an exception. Report queries, non-caching queries, and data read/modify queries are allowed.
4254     * <p>
4255     * On exception any global transaction will be rolled back or marked rollback only. No recovery of this UnitOfWork will be possible.
4256     * <p>
4257     * Can only be called once. It can not be used to write out changes in an incremental fashion.
4258     * <p>
4259     * Use to partially commit a transaction outside of a JTA transaction's callbacks. Allows you to get back any exception directly.
4260     * <p>
4261     * Use to commit a UnitOfWork in two stages.
4262     */

4263    public void writeChanges() {
4264        if (!isActive()) {
4265            throw ValidationException.inActiveUnitOfWork("writeChanges");
4266        }
4267        if (isAfterWriteChangesButBeforeCommit()) {
4268            throw ValidationException.cannotWriteChangesTwice();
4269        }
4270        if (isNestedUnitOfWork()) {
4271            throw ValidationException.writeChangesOnNestedUnitOfWork();
4272        }
4273        log(SessionLog.FINER, SessionLog.TRANSACTION, "begin_unit_of_work_commit");
4274        getEventManager().preCommitUnitOfWork();
4275        setLifecycle(CommitPending);
4276        try {
4277            commitToDatabaseWithChangeSet(false);
4278        } catch (RuntimeException JavaDoc e) {
4279            setLifecycle(WriteChangesFailed);
4280            throw e;
4281        }
4282        setLifecycle(CommitTransactionPending);
4283    }
4284
4285    /**
4286     * INTERNAL:
4287     * This method notifies the accessor that a particular sets of writes has
4288     * completed. This notification can be used for such thing as flushing the
4289     * batch mechanism
4290     */

4291    public void writesCompleted() {
4292        getParent().writesCompleted();
4293    }
4294
4295    /**
4296     * log the message and debug info if option is set. (reduce the duplicate codes)
4297     */

4298    private void logDebugMessage(Object JavaDoc object, String JavaDoc debugMessage) {
4299        log(SessionLog.FINEST, SessionLog.TRANSACTION, debugMessage, object);
4300    }
4301
4302    /**
4303     * INTERNAL:
4304     * Return the registered working copy from the unit of work identity map.
4305     * If not registered in the unit of work yet, return null
4306     */

4307    public Object JavaDoc getWorkingCopyFromUnitOfWorkIdentityMap(Object JavaDoc object, Vector primaryKey) {
4308        //return the descriptor of the passed object
4309
ClassDescriptor descriptor = getDescriptor(object);
4310        if (descriptor == null) {
4311            throw DescriptorException.missingDescriptor(object.getClass().toString());
4312        }
4313
4314        //aggregated object cannot be registered directly, but through the parent owning object.
4315
if (descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()) {
4316            throw ValidationException.cannotRegisterAggregateObjectInUnitOfWork(object.getClass());
4317        }
4318
4319        // Check if the working copy is again being registered in which case we return the same working copy
4320
Object JavaDoc registeredObject = getCloneMapping().get(object);
4321        if (registeredObject != null) {
4322            return object;
4323        }
4324
4325        //check the unit of work cache first to see if already registered.
4326
Object JavaDoc objectFromUOWCache = getIdentityMapAccessorInstance().getIdentityMapManager().getFromIdentityMap(primaryKey, object.getClass(), descriptor);
4327        if (objectFromUOWCache != null) {
4328            // Has already been cloned, return the working clone from the IM rather than the passed object.
4329
return objectFromUOWCache;
4330        }
4331
4332        //not found, return null
4333
return null;
4334    }
4335
4336    /**
4337     * INTERNAL:
4338     */

4339    public IdentityHashtable getPessimisticLockedObjects() {
4340        if (pessimisticLockedObjects == null) {
4341            // 2612538 - the default size of IdentityHashtable (32) is appropriate
4342
pessimisticLockedObjects = new IdentityHashtable();
4343        }
4344        return pessimisticLockedObjects;
4345    }
4346
4347    /**
4348     * INTERNAL:
4349     */

4350    public void addPessimisticLockedClone(Object JavaDoc clone) {
4351        log(SessionLog.FINEST, SessionLog.TRANSACTION, "tracking_pl_object", clone, new Integer JavaDoc(this.hashCode()));
4352        getPessimisticLockedObjects().put(clone, clone);
4353    }
4354
4355    /**
4356      * INTERNAL:
4357      */

4358    public boolean isPessimisticLocked(Object JavaDoc clone) {
4359        return getPessimisticLockedObjects().containsKey(clone);
4360    }
4361
4362    /**
4363     * INTERNAL:
4364     * True if either DataModifyQuery or ModifyAllQuery was executed.
4365     * In absense of transaction the query execution starts one, therefore
4366     * the flag may only be true in transaction, it's reset on commit or rollback.
4367     */

4368    public void setWasNonObjectLevelModifyQueryExecuted(boolean wasNonObjectLevelModifyQueryExecuted) {
4369        this.wasNonObjectLevelModifyQueryExecuted = wasNonObjectLevelModifyQueryExecuted;
4370    }
4371    
4372    /**
4373     * INTERNAL:
4374     * True if either DataModifyQuery or ModifyAllQuery was executed.
4375     */

4376    public boolean wasNonObjectLevelModifyQueryExecuted() {
4377        return wasNonObjectLevelModifyQueryExecuted;
4378    }
4379    
4380    /**
4381      * INTERNAL:
4382      * Indicates whether readObject should return the object read from the db
4383      * in case there is no object in uow cache (as opposed to fetching the object from
4384      * parent's cache). Note that wasNonObjectLevelModifyQueryExecuted()==true implies inTransaction()==true.
4385      */

4386    public boolean shouldReadFromDB() {
4387        return wasNonObjectLevelModifyQueryExecuted();
4388    }
4389}
4390
Popular Tags