FileDocCategorySizeDatePackage
RepeatableWriteUnitOfWork.javaAPI DocGlassfish v2 API20065Tue Jun 05 12:23:52 BST 2007oracle.toplink.essentials.internal.ejb.cmp3.base

RepeatableWriteUnitOfWork

public class RepeatableWriteUnitOfWork extends UnitOfWorkImpl

Fields Summary
protected UnitOfWorkChangeSet
cumulativeUOWChangeSet
Used to store the final UnitOfWorkChangeSet for merge into the shared cache
protected IdentityHashtable
unregisteredDeletedObjectsCloneToBackupAndOriginal
Used to store objects already deleted from the db and unregistered
protected boolean
shouldTerminateTransaction
Used to determine if UnitOfWork should commit and rollback transactions This is used when an EntityTransaction is controlling the transaction
protected boolean
shouldClearForCloseInsteadOfResume
Used to determine if UnitOfWork.synchronizeAndResume method should resume (the normal behaviour); or alternatively clear the UnitOfWork.
Constructors Summary
public RepeatableWriteUnitOfWork(AbstractSession parentSession)

    
      
        super(parentSession);
        this.shouldTerminateTransaction = true;
        this.shouldNewObjectsBeCached = true;
    
Methods Summary
public voidclearForClose(boolean shouldClearCache)
INTERNAL: Call this method if the uow will no longer used for comitting transactions: all the changes sets will be dereferenced, and (optionally) the cache cleared. If the uow is not released, but rather kept around for ValueHolders, then identity maps shouldn't be cleared: the parameter value should be 'false'. The lifecycle set to Birth so that uow ValueHolder still could be used. Alternatively, if called from release method then everything should go and therefore parameter value should be 'true'. In this case lifecycle won't change - uow.release (optionally) calls this method when it (uow) is already dead. The reason for calling this method from release is to free maximum memory right away: the uow might still be referenced by objects using UOWValueHolders (though they shouldn't be around they still might).

        super.clearForClose(shouldClearCache);
        this.cumulativeUOWChangeSet = null;
        this.unregisteredDeletedObjectsCloneToBackupAndOriginal = null;
    
public voidcommitRootUnitOfWork()
INTERNAL: Commit the changes to any objects to the parent.

        commitToDatabaseWithChangeSet(false);
        // unit of work has been committed so it's ok to set the cumulative into the UOW for merge
        if(this.cumulativeUOWChangeSet != null) {
            this.cumulativeUOWChangeSet.mergeUnitOfWorkChangeSet((UnitOfWorkChangeSet)this.getUnitOfWorkChangeSet(), this, true);
            setUnitOfWorkChangeSet(this.cumulativeUOWChangeSet);
        }

        commitTransactionAfterWriteChanges(); // this method will commit the transaction
                                              // and set the transaction flags appropriately

        // Merge after commit	
        mergeChangesIntoParent();
    
protected voidcommitToDatabase(boolean commitTransaction)
INTERNAL: Wraps the oracle.toplink.essentials.exceptions.OptimisticLockException in a javax.persistence.OptimisticLockException. This conforms to the EJB3 specs

param
commitTransaction

        try {
            super.commitToDatabase(commitTransaction);
        } catch (oracle.toplink.essentials.exceptions.OptimisticLockException ole) {
            throw new javax.persistence.OptimisticLockException(ole);
        }
    
public voidcommitTransaction()
INTERNAL: This is internal to the uow, transactions should not be used explictly in a uow. The uow shares its parents transactions.

        if (this.shouldTerminateTransaction || getParent().getTransactionMutex().isNested()){
            super.commitTransaction();
        }
    
public voiddiscoverUnregisteredNewObjects(java.lang.Object clone, oracle.toplink.essentials.internal.helper.IdentityHashtable knownNewObjects, oracle.toplink.essentials.internal.helper.IdentityHashtable unregisteredExistingObjects, oracle.toplink.essentials.internal.helper.IdentityHashtable visitedObjects)
INTERNAL: Traverse the object to find references to objects not registered in this unit of work.

        // This define an inner class for process the itteration operation, don't be scared, its just an inner class.
        DescriptorIterator iterator = new DescriptorIterator() {
            public void iterate(Object object) {
                // If the object is read-only the do not continue the traversal.
                if (isClassReadOnly(object.getClass()) || isObjectDeleted(object)) {
                    this.setShouldBreak(true);
                    return;
                }
                //check for null mapping, this may be the first iteration
                if ((getCurrentMapping() != null) && ((ForeignReferenceMapping)getCurrentMapping()).isCascadePersist() ){
                    ((RepeatableWriteUnitOfWork)getSession()).registerNewObjectForPersist(object, new IdentityHashtable());
                }else{
                    if (!getCloneMapping().containsKey(object)){
                        if (! checkForUnregisteredExistingObject(object)){
                            throw new IllegalStateException(ExceptionLocalization.buildMessage("new_object_found_during_commit", new Object[]{object}));
                        }else{
                            ((IdentityHashtable)getUnregisteredExistingObjects()).put(object, object);
                            this.setShouldBreak(true);
                            return;
                        }
                    }
                }
            }
        };

        //set the collection in the UnitofWork to be this list
        setUnregisteredExistingObjects(unregisteredExistingObjects);

        iterator.setVisitedObjects(visitedObjects);
        iterator.setResult(knownNewObjects);
        iterator.setSession(this);
        // When using wrapper policy in EJB the iteration should stop on beans,
        // this is because EJB forces beans to be registered anyway and clone identity can be violated
        // and the violated clones references to session objects should not be traversed.
        iterator.setShouldIterateOverWrappedObjects(false);
        iterator.startIterationOn(clone);
    
public java.lang.ObjectgetUnregisteredDeletedCloneForOriginal(java.lang.Object original)
INTERNAL: Called only by UnitOfWorkIdentityMapAccessor.getAndCloneCacheKeyFromParent method. Return unregisteredDeletedClone corresponding to the passed original, or null

        if(unregisteredDeletedObjectsCloneToBackupAndOriginal != null) {
            Enumeration keys = unregisteredDeletedObjectsCloneToBackupAndOriginal.keys();
            Enumeration values = unregisteredDeletedObjectsCloneToBackupAndOriginal.elements();
            while(keys.hasMoreElements()) {
                Object deletedObjectClone = keys.nextElement();
                Object[] backupAndOriginal = (Object[])values.nextElement();
                Object currentOriginal = backupAndOriginal[1];
                if(original == currentOriginal) {
                    return deletedObjectClone;
                }
            }
        }
        return null;
    
public booleanisAfterWriteChangesButBeforeCommit()
INTERNAL: Has writeChanges() been attempted on this UnitOfWork? It may have either suceeded or failed but either way the UnitOfWork is in a highly restricted state.

        //dont' check for writechanges failure.
        return (getLifecycle() == CommitTransactionPending);
    
public booleanisObjectDeleted(java.lang.Object object)
INTERNAL: Return if the object has been deleted in this unit of work.

        if(super.isObjectDeleted(object)) {
            return true;
        } else {
            if(unregisteredDeletedObjectsCloneToBackupAndOriginal != null) {
                if(unregisteredDeletedObjectsCloneToBackupAndOriginal.containsKey(object)) {
                    return true;
                }
            }
            if (hasObjectsDeletedDuringCommit()) {
                return getObjectsDeletedDuringCommit().containsKey(object);
            } else {
                return false;
            }
        }
    
public voidissueSQLbeforeCompletion()
INTERNAL: For synchronized units of work, dump SQL to database


        super.issueSQLbeforeCompletion(false);

        if (this.cumulativeUOWChangeSet != null && this.getUnitOfWorkChangeSet() != null){
            // unit of work has been committed so it's ok to set the cumulative into the UOW for merge
            this.cumulativeUOWChangeSet.mergeUnitOfWorkChangeSet((UnitOfWorkChangeSet)this.getUnitOfWorkChangeSet(), this, true);
            setUnitOfWorkChangeSet(this.cumulativeUOWChangeSet);
        }

        commitTransactionAfterWriteChanges(); // this method will commit the transaction
                                              // and set the transaction flags appropriately
    
public java.lang.ObjectmergeCloneWithReferences(java.lang.Object rmiClone, int cascadePolicy, boolean forceCascade)
INTERNAL: Merge the attributes of the clone into the unit of work copy.

        Object mergedObject = super.mergeCloneWithReferences(rmiClone, cascadePolicy, forceCascade);
        
        if (mergedObject != null) {
            // This will assign a sequence number to the merged object if it 
            // doesn't already have one (that is, it is a new object).
            assignSequenceNumber(mergedObject);
        }
        
        return mergedObject;
    
protected voidregisterNotRegisteredNewObjectForPersist(java.lang.Object newObject, oracle.toplink.essentials.descriptors.ClassDescriptor descriptor)
INTERNAL: Called only by registerNewObjectForPersist method, and only if newObject is not already registered. If newObject is found in unregisteredDeletedObjectsCloneToBackupAndOriginal then it's re-registered, otherwise the superclass method called.

        if(unregisteredDeletedObjectsCloneToBackupAndOriginal != null) {
            Object[] backupAndOriginal = (Object[])unregisteredDeletedObjectsCloneToBackupAndOriginal.remove(newObject);
            if(backupAndOriginal != null) {
                // backup
                getCloneMapping().put(newObject, backupAndOriginal[0]);
                // original
                registerNewObjectClone(newObject, backupAndOriginal[1], descriptor);

                // Check if the new objects should be cached.
                registerNewObjectInIdentityMap(newObject, newObject);
                
                return;
            }
        }
        super.registerNotRegisteredNewObjectForPersist(newObject, descriptor);
    
public voidrollbackTransaction()
INTERNAL: This is internal to the uow, transactions should not be used explictly in a uow. The uow shares its parents transactions.

        if (this.shouldTerminateTransaction || getParent().getTransactionMutex().isNested()){
            super.rollbackTransaction();
        }else{
            //rollback called which means txn failed.
            //but rollback was stopped by entitytransaction which means the
            //transaction will want to call release later.  Make sure release
            //will rollback transaction.
            setWasTransactionBegunPrematurely(true);
        }
    
public voidsetShouldClearForCloseInsteadOfResume(boolean shouldClearForCloseInsteadOfResume)

        this.shouldClearForCloseInsteadOfResume = shouldClearForCloseInsteadOfResume;
    
public voidsetShouldTerminateTransaction(boolean shouldTerminateTransaction)

        this.shouldTerminateTransaction = shouldTerminateTransaction;
    
public booleanshouldClearForCloseInsteadOfResume()

        return shouldClearForCloseInsteadOfResume;
    
public booleanshouldClearForCloseOnRelease()
INTERNAL: Indicates whether clearForClose methor should be called by release method.

        return true;
    
public voidsynchronizeAndResume()
INTERNAL Synchronize the clones and update their backup copies. Called after commit and commit and resume.

        if(this.shouldClearForCloseInsteadOfResume()) {
            this.clearForClose(false);
        } else {
            this.cumulativeUOWChangeSet = null;
            this.unregisteredDeletedObjectsCloneToBackupAndOriginal = null;
            super.synchronizeAndResume();
        }
    
public voidupdateChangeTrackersIfRequired(java.lang.Object objectToWrite, oracle.toplink.essentials.internal.sessions.ObjectChangeSet changeSetToWrite, oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl uow, oracle.toplink.essentials.descriptors.ClassDescriptor descriptor)
INTERNAL: This method is used internally to update the tracked objects if required

        descriptor.getObjectChangePolicy().updateWithChanges(objectToWrite, changeSetToWrite, uow, descriptor);
    
public voidwriteChanges()
INTERNAL: This method will cause the all of the tracked objects at this level to have their changes written to the database. It will then decrement the depth level.

            if(unregisteredDeletedObjectsCloneToBackupAndOriginal == null) {
                unregisteredDeletedObjectsCloneToBackupAndOriginal = new IdentityHashtable(2);
            }
            IdentityHashtable allObjectsList = new IdentityHashtable();
            IdentityHashtable visitedNodes = new IdentityHashtable(2);
            IdentityHashtable newObjects = new IdentityHashtable(2);
            IdentityHashtable existingObjects = new IdentityHashtable(2);
            IdentityHashtable insertedNewObjects = new IdentityHashtable(2);
            for (Enumeration clones = getCloneMapping().keys(); clones.hasMoreElements();){
                Object object = clones.nextElement();
                discoverUnregisteredNewObjects(object, newObjects, existingObjects, visitedNodes);
                allObjectsList.put(object, object);
            }
            for (Enumeration newClones = getNewObjectsCloneToOriginal().keys();newClones.hasMoreElements();){
                Object object = newClones.nextElement();
                assignSequenceNumber(object);
                insertedNewObjects.put(object, object);
                // add potentially newly discovered new objects
                allObjectsList.put(object, object);
            }

            if (getUnitOfWorkChangeSet() == null) {
                setUnitOfWorkChangeSet(new UnitOfWorkChangeSet());
            }
            calculateChanges(allObjectsList, (UnitOfWorkChangeSet)getUnitOfWorkChangeSet());
            // write those changes to the database.
            UnitOfWorkChangeSet changeSet = (UnitOfWorkChangeSet)getUnitOfWorkChangeSet();
            if (!changeSet.hasChanges() && !changeSet.hasForcedChanges() && ! this.hasDeletedObjects() && ! this.hasModifyAllQueries()){
            	return;
            }
            try {
                commitToDatabaseWithPreBuiltChangeSet(changeSet, false);
                this.writesCompleted();
            } catch (RuntimeException ex) {
                setLifecycle(WriteChangesFailed);
                throw ex;
            }
            //bug 4730595: fix puts deleted objects in the UnitOfWorkChangeSet as they are removed.
            getDeletedObjects().clear();

            // unregister all deleted objects,
            // keep them along with their original and backup values in unregisteredDeletedObjectsCloneToBackupAndOriginal
            Enumeration enumDeleted = getObjectsDeletedDuringCommit().keys();
            while(enumDeleted.hasMoreElements()) {
                Object deletedObject = enumDeleted.nextElement();
                Object[] backupAndOriginal = {getCloneMapping().get(deletedObject), getCloneToOriginals().get(deletedObject)};
                unregisteredDeletedObjectsCloneToBackupAndOriginal.put(deletedObject, backupAndOriginal);
                unregisterObject(deletedObject);
            }
            getObjectsDeletedDuringCommit().clear();

            if(this.cumulativeUOWChangeSet == null) {
                this.cumulativeUOWChangeSet = (UnitOfWorkChangeSet)getUnitOfWorkChangeSet();
            } else {
                //merge those changes back into the backup clones and the final uowChangeSet
                this.cumulativeUOWChangeSet.mergeUnitOfWorkChangeSet((UnitOfWorkChangeSet)getUnitOfWorkChangeSet(), this, true);
            }
            //clean up
            setUnitOfWorkChangeSet(new UnitOfWorkChangeSet());
            Enumeration enumtr = insertedNewObjects.elements();
            while (enumtr.hasMoreElements()) {
                Object clone = enumtr.nextElement();
                Object original = getNewObjectsCloneToOriginal().remove(clone);
                if (original != null) {
                    getNewObjectsOriginalToClone().remove(original);
                    //no longer new to this unit of work 
                    getCloneToOriginals().put(clone, original);
                }
            }