Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java?rev=422235&r1=422234&r2=422235&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/odmg/ObjectEnvelopeTable.java Sat Jul 15 07:28:03 2006 @@ -1,6 +1,6 @@ package org.apache.ojb.odmg; -/* Copyright 2002-2004 The Apache Software Foundation +/* Copyright 2002-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,12 @@ import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.commons.lang.SystemUtils; import org.apache.ojb.broker.Identity; import org.apache.ojb.broker.OJBRuntimeException; import org.apache.ojb.broker.OptimisticLockException; import org.apache.ojb.broker.PersistenceBroker; import org.apache.ojb.broker.PersistenceBrokerInternal; -import org.apache.ojb.broker.accesslayer.ConnectionManagerIF; import org.apache.ojb.broker.core.proxy.CollectionProxy; import org.apache.ojb.broker.core.proxy.IndirectionHandler; import org.apache.ojb.broker.metadata.ClassDescriptor; @@ -40,27 +40,20 @@ import org.apache.ojb.broker.util.logging.LoggerFactory; import org.apache.ojb.odmg.link.LinkEntry; import org.apache.ojb.odmg.link.LinkEntryMtoN; -import org.apache.ojb.odmg.states.StateOldClean; -import org.apache.ojb.odmg.states.StateTransient; +import org.apache.ojb.odmg.states.ModificationState; import org.odmg.LockNotGrantedException; import org.odmg.ODMGRuntimeException; import org.odmg.Transaction; import org.odmg.TransactionAbortedException; /** - * manages all ObjectEnvelopes included by a transaction. + * Manages all ObjectEnvelopes included by a transaction. * Performs commit, and rollack operations on all included Envelopes. - * - * @author Thomas Mahler - * @author <a href="mailto:[EMAIL PROTECTED]">Matthew Baird</a> - * - * MBAIRD: added explicit closing and de-referencing to prevent any - * GC issues. */ -public class ObjectEnvelopeTable +class ObjectEnvelopeTable { private Logger log = LoggerFactory.getLogger(ObjectEnvelopeTable.class); - private TransactionImpl transaction; + private final TransactionImpl transaction; /** * A list of [EMAIL PROTECTED] org.apache.ojb.broker.Identity} objects which are @@ -68,35 +61,32 @@ * as "delete". E.g. if a collection reference C is moved from object A1 to A2, * then A1 wants to "delete" C and A2 wants to mark the new C object as "new". */ - private List newAssociatedIdentites = new ArrayList(); - private List m2nLinkList = new ArrayList(); - private List m2nUnlinkList = new ArrayList(); - private List markedForDeletionList = new ArrayList(); - private List markedForInsertList = new ArrayList(); + private final List newAssociatedIdentites = new ArrayList(); + private final List m2nLinkList = new ArrayList(); + private final List m2nUnlinkList = new ArrayList(); /** - * the internal table mapping Objects to their ObjectTransactionWrappers + * This internal table map the object identity to the object + * transactional wrapper. This map is associated with the + * [EMAIL PROTECTED] #objectEnvelopesOrderList}. */ - private Map mhtObjectEnvelopes = new HashMap(); - + private Map objectEnvelopesMap; /** - * a vector containing the ObjectEnvelope objects representing modifications + * a list containing the ObjectEnvelope objects representing modifications * in the order they were added. If an ObjectEnvelope is added twice, only - * the the second addition is ignored. + * the the second addition is ignored. This list is associated with + * the [EMAIL PROTECTED] #objectEnvelopesMap}. */ - private ArrayList mvOrderOfIds = new ArrayList(); + private ArrayList objectEnvelopesOrderList; - /** - * marker used to avoid superfluous reordering and commiting - */ - private boolean needsCommit = false; + /** marker used to avoid superfluous reordering and commiting */ + private boolean needsCommit; - /** - * Creates new ObjectEnvelopeTable - */ - public ObjectEnvelopeTable(TransactionImpl myTransaction) + /** Creates new ObjectEnvelopeTable */ + ObjectEnvelopeTable(TransactionImpl myTransaction) { transaction = myTransaction; + prepareForUse(); } TransactionImpl getTransaction() @@ -105,23 +95,66 @@ } /** - * prepare this instance for reuse + * Returns the number of registered objects. */ - public void refresh() + public int registeredObjectCount() + { + return objectEnvelopesMap.size(); + } + + /** prepare this instance for re-/use */ + void prepareForUse() { needsCommit = false; - mhtObjectEnvelopes = new HashMap(); - mvOrderOfIds = new ArrayList(); - afterWriteCleanup(); + objectEnvelopesMap = new HashMap(); + objectEnvelopesOrderList = new ArrayList(); + linkCleanup(); } - void afterWriteCleanup() - { - m2nLinkList.clear(); - m2nUnlinkList.clear(); - newAssociatedIdentites.clear(); - markedForDeletionList.clear(); - markedForInsertList.clear(); + /** + * Call this after the transaction is completed. + */ + void afterTransactionCleanup() + { + this.objectEnvelopesMap = null; + this.objectEnvelopesOrderList = null; + } + + /** + * Call this after the objects are written to datastore. + */ + private void linkCleanup() + { + if(m2nLinkList.size() > 0) m2nLinkList.clear(); + if(m2nUnlinkList.size() > 0) m2nUnlinkList.clear(); + if(newAssociatedIdentites.size() > 0) newAssociatedIdentites.clear(); + } + + /** + * This method have to be called to reuse all registered [EMAIL PROTECTED] ObjectEnvelope} + * objects after transaction commit/flush/checkpoint call. + */ + private void prepareForReuse(boolean reuse) + { + if(reuse) + { + // using clone to avoid ConcurentModificationException + Iterator iter = ((List) objectEnvelopesOrderList.clone()).iterator(); + while(iter.hasNext()) + { + ObjectEnvelope mod = (ObjectEnvelope) iter.next(); + ModificationState state = mod.getModificationState(); + if(!needsCommit || (state.isClean() && !state.isNew()) || state.isTransient()) + { + // nothing to do + } + else + { + mod.setModificationState(state.markClean()); + } + } + } + linkCleanup(); } /** @@ -133,24 +166,17 @@ public void writeObjects(boolean reuse) throws TransactionAbortedException, LockNotGrantedException { PersistenceBroker broker = transaction.getBroker(); - ConnectionManagerIF connMan = broker.serviceConnectionManager(); boolean saveBatchMode = broker.serviceBatchManager().isBatchMode(); try { - if (log.isDebugEnabled()) + if(log.isDebugEnabled()) { log.debug( - "PB is in internal tx: " - + broker.isInTransaction() - + " broker was: " - + broker); - } - // all neccessary db operations are executed within a PersistenceBroker transaction: - if (!broker.isInTransaction()) - { - log.error("PB associated with current odmg-tx is not in tx"); - throw new TransactionAbortedException("Underlying PB is not in tx, was begin call done before commit?"); + "PB is in internal tx: " + + broker.isInTransaction() + + " broker was: " + + broker); } // Committing has to be done in two phases. First implicitly upgrade to lock on all related @@ -174,12 +200,6 @@ // 4. Reorder objects reorder(); -// System.out.println("## ordering: "); -// for(int i = 0; i < mvOrderOfIds.size(); i++) -// { -// System.out.println("" + mvOrderOfIds.get(i)); -// } -// System.out.println("## ordering end"); // 5. write objects. writeAllEnvelopes(reuse); @@ -189,12 +209,8 @@ // 7. Update all Envelopes to new CleanState prepareForReuse(reuse); - - // 6. commit cleanup - afterWriteCleanup(); - } - catch (Exception e) + catch(Exception e) { broker.serviceBatchManager().cancelBatch(); /* @@ -229,9 +245,7 @@ } } - /** - * commit all envelopes against the current broker - */ + /** commit all envelopes against the current broker */ private void writeAllEnvelopes(boolean reuse) { // perform remove of m:n indirection table entries first @@ -239,25 +253,26 @@ Iterator iter; // using clone to avoid ConcurentModificationException - iter = ((List) mvOrderOfIds.clone()).iterator(); - while (iter.hasNext()) - { - ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); - if(needsCommit) + iter = ((List) objectEnvelopesOrderList.clone()).iterator(); + while(iter.hasNext()) { - boolean insert = mod.needsInsert(); - mod.getModificationState().commit(mod); - if(reuse && insert) - { - getTransaction().doSingleLock(mod.getClassDescriptor(), mod.getObject(), mod.getIdentity(), Transaction.WRITE); + ObjectEnvelope mod = (ObjectEnvelope) iter.next(); + boolean insert = false; + if(needsCommit) + { + insert = mod.needsInsert(); + mod.getModificationState().commit(mod); + if(reuse && insert) + { + getTransaction().internalSingleLock(mod.getClassDescriptor(), mod.getIdentity(), Transaction.WRITE); + } } - } - /* - arminw: important to call this cleanup method for each registered - ObjectEnvelope, because this method will e.g. remove proxy listener - objects for registered objects. - */ - mod.cleanup(reuse); + /* + arminw: important to call this cleanup method for each registered + ObjectEnvelope, because this method will e.g. remove proxy listener + objects for registered objects. + */ + mod.cleanup(reuse, insert); } // add m:n indirection table entries performM2NLinkEntries(); @@ -265,38 +280,22 @@ /** * Mark objects no longer available in collection for delete and new objects for insert. + * * @param broker the PB to persist all objects */ private void checkAllEnvelopes(PersistenceBroker broker) { - Iterator iter = ((List) mvOrderOfIds.clone()).iterator(); - while (iter.hasNext()) - { - ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); - mod.markReferenceElements(broker); - } - } - - /** - * This method have to be called to reuse all registered [EMAIL PROTECTED] ObjectEnvelope} - * objects after transaction commit/flush/checkpoint call. - */ - private void prepareForReuse(boolean reuse) - { - if(reuse) + Iterator iter = ((List) objectEnvelopesOrderList.clone()).iterator(); + while(iter.hasNext()) { - // using clone to avoid ConcurentModificationException - Iterator iter = ((List) mvOrderOfIds.clone()).iterator(); - while (iter.hasNext()) + ObjectEnvelope mod = (ObjectEnvelope) iter.next(); + // transient and NewDelete objects must NOT performed + ModificationState state = mod.getModificationState(); + if(!state.isTransient() && !(state.isNew() && state.isDelete())) { - ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); - mod.refreshObjectImage(); - if(needsCommit && (mod.getModificationState() != StateOldClean.getInstance() || mod.getModificationState() != StateTransient.getInstance())) - { - mod.setModificationState(mod.getModificationState().markClean()); - } + mod.markReferenceElements(broker); } - } + } } /** @@ -307,49 +306,61 @@ private void upgradeLockIfNeeded() { // using clone to avoid ConcurentModificationException - Iterator iter = ((List) mvOrderOfIds.clone()).iterator(); + Iterator iter = ((List) objectEnvelopesOrderList.clone()).iterator(); + TransactionImpl tx = getTransaction(); ObjectEnvelope mod; - while (iter.hasNext()) + while(iter.hasNext()) { - mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); - /* - now we check if all modified objects has a write lock. On insert of new - objects we don't need a write lock. - */ - if(!mod.needsInsert()) + mod = (ObjectEnvelope) iter.next(); + // no need to lock new objects + if(mod.getModificationState().isNew()) { - if((mod.needsDelete() || mod.needsUpdate() - || mod.hasChanged(getTransaction().getBroker()))) + needsCommit = true; + } + // ignore transient objects + else if(!mod.getModificationState().isTransient()) + { + /* + now we check if all modified objects has a write lock. On insert of new + objects we don't need a write lock. + */ + if(!mod.needsInsert()) + { + if((mod.needsDelete() || mod.needsUpdate() + || mod.hasChanged(tx.getBroker()))) + { + needsCommit = true; + // mark object dirty + mod.setModificationState(mod.getModificationState().markDirty()); + ClassDescriptor cld = mod.getClassDescriptor(); + // if the object isn't already locked, we will do it now + if(!mod.isWriteLocked()) + { + tx.internalSingleLock(cld, mod.getIdentity(), Transaction.WRITE); + } + } + } + else { needsCommit = true; - // mark object dirty - mod.setModificationState(mod.getModificationState().markDirty()); - ClassDescriptor cld = mod.getClassDescriptor(); - if(!mod.isWriteLocked()) getTransaction().doSingleLock(cld, mod.getObject(), mod.getIdentity(), Transaction.WRITE); } } - else - { - needsCommit = true; - } } } - /** - * perform rollback on all tx-states - */ + /** perform rollback on all tx-states */ public void rollback() { try { - Iterator iter = mvOrderOfIds.iterator(); - while (iter.hasNext()) + Iterator iter = objectEnvelopesOrderList.iterator(); + while(iter.hasNext()) { - ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next()); - if (log.isDebugEnabled()) + ObjectEnvelope mod = (ObjectEnvelope) iter.next(); + if(log.isDebugEnabled()) log.debug("rollback: " + mod); // if the Object has been modified by transaction, mark object as dirty - if (mod.hasChanged(transaction.getBroker())) + if(mod.hasChanged(transaction.getBroker())) { mod.setModificationState(mod.getModificationState().markDirty()); } @@ -360,16 +371,13 @@ { needsCommit = false; } - afterWriteCleanup(); } - /** - * remove an objects entry from the Hashtable - */ + /** remove an objects entry from the object registry */ public void remove(Object pKey) { Identity id; - if (pKey instanceof Identity) + if(pKey instanceof Identity) { id = (Identity) pKey; } @@ -377,8 +385,8 @@ { id = transaction.getBroker().serviceIdentity().buildIdentity(pKey); } - mhtObjectEnvelopes.remove(id); - mvOrderOfIds.remove(id); + Object toRemove = objectEnvelopesMap.remove(id); + objectEnvelopesOrderList.remove(toRemove); } /** @@ -389,20 +397,25 @@ */ public Enumeration elements() { - return java.util.Collections.enumeration(mhtObjectEnvelopes.values()); + return java.util.Collections.enumeration(objectEnvelopesMap.values()); } - /** - * retrieve an objects ObjectModification state from the hashtable - */ + /** retrieve an objects ObjectModification state from the hashtable */ public ObjectEnvelope getByIdentity(Identity id) { - return (ObjectEnvelope) mhtObjectEnvelopes.get(id); + return (ObjectEnvelope) objectEnvelopesMap.get(id); + } + + /** retrieve an objects ObjectModification state from the hashtable */ + public boolean contains(Identity oid) + { + return objectEnvelopesMap.containsKey(oid); } /** * retrieve an objects ObjectEnvelope state from the hashtable. * If no ObjectEnvelope is found, a new one is created and returned. + * * @return the resulting ObjectEnvelope */ public ObjectEnvelope get(Object pKey, boolean isNew) @@ -415,82 +428,74 @@ /** * retrieve an objects ObjectEnvelope state from the hashtable. * If no ObjectEnvelope is found, a new one is created and returned. + * * @return the resulting ObjectEnvelope */ public ObjectEnvelope get(Identity oid, Object pKey, boolean isNew) { ObjectEnvelope result = getByIdentity(oid); - if (result == null) + if(result == null) { result = new ObjectEnvelope(this, oid, pKey, isNew); - mhtObjectEnvelopes.put(oid, result); - mvOrderOfIds.add(oid); - if (log.isDebugEnabled()) - log.debug("register: " + result); + objectEnvelopesMap.put(oid, result); + objectEnvelopesOrderList.add(result); + if(log.isDebugEnabled()) log.debug("Register: " + result); } return result; } - /** - * Returns a String representation of this object - */ + /** Returns a String representation of this object */ public String toString() { ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE); - buf.append("### ObjectEnvelopeTable dump:"); + String eol = SystemUtils.LINE_SEPARATOR; + buf.append("# ObjectEnvelopeTable dump:" + eol + "start["); Enumeration en = elements(); - while (en.hasMoreElements()) + while(en.hasMoreElements()) { ObjectEnvelope mod = (ObjectEnvelope) en.nextElement(); - buf.append(mod.toString()); + buf.append(mod.toString() + eol); } + buf.append("]end"); return buf.toString(); } - /** - * retrieve an objects ObjectModification state from the hashtable - */ - public boolean contains(Identity oid) - { - //Integer keyInteger = new Integer(System.identityHashCode(key)); - return mhtObjectEnvelopes.containsKey(oid); - } - - /** - * Reorder the objects in the table to resolve referential integrity dependencies. - */ - private void reorder() + /** Reorder the objects in the table to resolve referential integrity dependencies. */ + private void reorder() { - if (getTransaction().isOrdering() && needsCommit && mhtObjectEnvelopes.size() > 1) + if(getTransaction().isOrdering() && needsCommit && objectEnvelopesMap.size() > 1) { - ObjectEnvelopeOrdering ordering = new ObjectEnvelopeOrdering( - getTransaction().getBrokerInternal(), mvOrderOfIds, mhtObjectEnvelopes); + ObjectEnvelopeOrdering ordering = new ObjectEnvelopeOrdering(objectEnvelopesOrderList); ordering.reorder(); - Identity[] newOrder = ordering.getOrdering(); - - mvOrderOfIds.clear(); - for (int i = 0; i < newOrder.length; i++) + ObjectEnvelope[] newOrder = ordering.getOrdering(); + + objectEnvelopesOrderList.clear(); + for(int i = 0; i < newOrder.length; i++) { - mvOrderOfIds.add(newOrder[i]); + objectEnvelopesOrderList.add(newOrder[i]); } } } void cascadingDependents() { - Iterator it = mhtObjectEnvelopes.values().iterator(); + Iterator it = objectEnvelopesMap.values().iterator(); ObjectEnvelope mod; + List markedForDeletion = null; + List markedForInsert = null; // first we search for all deleted/insert objects while(it.hasNext()) { - mod = (ObjectEnvelope) it.next(); + mod = (ObjectEnvelope) it.next(); if(mod.needsDelete()) { - addForDeletionDependent(mod); + if(markedForDeletion == null) markedForDeletion = new ArrayList(); + markedForDeletion.add(mod); } else if(mod.needsInsert()) { - addForInsertDependent(mod); + if(markedForInsert == null) markedForInsert = new ArrayList(); + markedForInsert.add(mod); } } /* @@ -498,67 +503,53 @@ then insert is mandatory, because the user could move unmaterialized collection proxy objects from one existing, which was deleted, to a new object. In this case the proxy was materialized on deletion of the main object, but on performing - the cascading insert the collection objects will be found assigned to the new object. + the cascading insert the collection objects will be found and assigned to the new object. */ - cascadeMarkedForDeletion(); - cascadeMarkedForInsert(); - } - - void addNewAssociatedIdentity(Identity oid) - { - newAssociatedIdentites.add(oid); - } - - boolean isNewAssociatedObject(Identity oid) - { - return newAssociatedIdentites.contains(oid); - } - - void addForInsertDependent(ObjectEnvelope mod) - { - markedForInsertList.add(mod); - } - - /** - * Starts recursive insert on all insert objects object graph - */ - private void cascadeMarkedForInsert() - { - // This list was used to avoid endless recursion on circular references - List alreadyPrepared = new ArrayList(); - for(int i = 0; i < markedForInsertList.size(); i++) + if(markedForDeletion != null) + { + /* Starts recursive delete on all delete objects object graph */ + for(int i = 0; i < markedForDeletion.size(); i++) + { + mod = (ObjectEnvelope) markedForDeletion.get(i); + // if the object wasn't associated with another object, start cascade delete + if(!isNewAssociatedObject(mod.getIdentity())) + { + cascadeDeleteFor(mod); + } + } + } + if(markedForInsert != null) { - ObjectEnvelope mod = (ObjectEnvelope) markedForInsertList.get(i); - // only if a new object was found we cascade to register the dependent objects - if(mod.needsInsert()) + /* Starts recursive insert on all insert objects object graph */ + for(int i = 0; i < markedForInsert.size(); i++) { - cascadeInsertFor(mod, alreadyPrepared); - alreadyPrepared.clear(); + mod = (ObjectEnvelope) markedForInsert.get(i); + // we expect that the list only contains objects for insert + cascadeInsertFor(mod); } } - markedForInsertList.clear(); } /** * Walk through the object graph of the specified insert object. Was used for * recursive object graph walk. */ - private void cascadeInsertFor(ObjectEnvelope mod, List alreadyPrepared) + private void cascadeInsertFor(ObjectEnvelope mod) { - // avoid endless recursion, so use List for registration - if(alreadyPrepared.contains(mod.getIdentity())) return; - alreadyPrepared.add(mod.getIdentity()); + // avoid endless recursion + if(mod.isMarkedForCascadingInsert()) return; + mod.markForCascadingInsert(); ClassDescriptor cld = getTransaction().getBroker().getClassDescriptor(mod.getObject().getClass()); List refs = cld.getObjectReferenceDescriptors(true); - cascadeInsertSingleReferences(mod, refs, alreadyPrepared); + cascadeInsertSingleReferences(mod, refs); List colls = cld.getCollectionDescriptors(true); - cascadeInsertCollectionReferences(mod, colls, alreadyPrepared); + cascadeInsertCollectionReferences(mod, colls); } - private void cascadeInsertSingleReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) + private void cascadeInsertSingleReferences(ObjectEnvelope source, List descriptor) { for(int i = 0; i < descriptor.size(); i++) { @@ -567,7 +558,7 @@ if(depObj != null) { - // in any case we have to link the source object when the object is insert + // in any case we have to link the source object when the object needs insert source.addLinkOneToOne(ord, false); IndirectionHandler handler = getTransaction() @@ -585,30 +576,23 @@ { rt = new RuntimeObject(depObj, getTransaction()); } + Identity oid = rt.getIdentity(); - if(!alreadyPrepared.contains(oid)) + ObjectEnvelope depMod = getByIdentity(oid); + // if the object isn't registered and is a new + // object, register it - else we have nothing to do + if(depMod == null && rt.isNew()) { - ObjectEnvelope depMod = getByIdentity(oid); - // if the object isn't registered and is a new object, register it - // else we have nothing to do - if(depMod == null && rt.isNew()) - { - getTransaction().lockAndRegister(rt, Transaction.WRITE, false, getTransaction().getRegistrationList()); - depMod = getByIdentity(oid); - } - if(depMod == null) - { - // should never occur - throw new RuntimeException("Unexpected behavior"); - } - cascadeInsertFor(depMod, alreadyPrepared); + getTransaction().lockAndRegister(rt, Transaction.WRITE, false); + depMod = getByIdentity(oid); + cascadeInsertFor(depMod); } } } } } - private void cascadeInsertCollectionReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) + private void cascadeInsertCollectionReferences(ObjectEnvelope source, List descriptor) { PersistenceBrokerInternal pb = getTransaction().getBrokerInternal(); for(int i = 0; i < descriptor.size(); i++) @@ -626,7 +610,7 @@ Iterator it = BrokerHelper.getCollectionIterator(pb, collOrArray); while(it.hasNext()) { - Object colObj = it.next(); + Object colObj = it.next(); if(colObj != null) { RuntimeObject rt = new RuntimeObject(colObj, getTransaction()); @@ -650,7 +634,7 @@ ObjectEnvelope oe = getByIdentity(oid); if(oe == null) { - getTransaction().lockAndRegister(rt, Transaction.WRITE, false, getTransaction().getRegistrationList()); + getTransaction().lockAndRegister(rt, Transaction.WRITE, false); oe = getByIdentity(oid); } if(col.isMtoNRelation()) @@ -668,7 +652,7 @@ */ oe.setModificationState(oe.getModificationState().markDirty()); } - cascadeInsertFor(oe, alreadyPrepared); + cascadeInsertFor(oe); } } } @@ -676,51 +660,26 @@ } } - void addForDeletionDependent(ObjectEnvelope mod) - { - markedForDeletionList.add(mod); - } - - /** - * Starts recursive delete on all delete objects object graph - */ - private void cascadeMarkedForDeletion() - { - List alreadyPrepared = new ArrayList(); - for(int i = 0; i < markedForDeletionList.size(); i++) - { - ObjectEnvelope mod = (ObjectEnvelope) markedForDeletionList.get(i); - // if the object wasn't associated with another object, start cascade delete - if(!isNewAssociatedObject(mod.getIdentity())) - { - cascadeDeleteFor(mod, alreadyPrepared); - alreadyPrepared.clear(); - } - } - markedForDeletionList.clear(); - } - /** * Walk through the object graph of the specified delete object. Was used for * recursive object graph walk. */ - private void cascadeDeleteFor(ObjectEnvelope mod, List alreadyPrepared) + private void cascadeDeleteFor(ObjectEnvelope mod) { // avoid endless recursion - if(alreadyPrepared.contains(mod.getIdentity())) return; - - alreadyPrepared.add(mod.getIdentity()); + if(mod.isMarkedForCascadingDelete()) return; + mod.markForCascadingDelete(); ClassDescriptor cld = getTransaction().getBroker().getClassDescriptor(mod.getObject().getClass()); List refs = cld.getObjectReferenceDescriptors(true); - cascadeDeleteSingleReferences(mod, refs, alreadyPrepared); + cascadeDeleteSingleReferences(mod, refs); List colls = cld.getCollectionDescriptors(true); - cascadeDeleteCollectionReferences(mod, colls, alreadyPrepared); + cascadeDeleteCollectionReferences(mod, colls); } - private void cascadeDeleteSingleReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) + private void cascadeDeleteSingleReferences(ObjectEnvelope source, List descriptor) { for(int i = 0; i < descriptor.size(); i++) { @@ -737,14 +696,14 @@ { ObjectEnvelope depMod = get(oid, depObj, false); depMod.setModificationState(depMod.getModificationState().markDelete()); - cascadeDeleteFor(depMod, alreadyPrepared); + cascadeDeleteFor(depMod); } } } } } - private void cascadeDeleteCollectionReferences(ObjectEnvelope source, List descriptor, List alreadyPrepared) + private void cascadeDeleteCollectionReferences(ObjectEnvelope source, List descriptor) { PersistenceBrokerInternal pb = getTransaction().getBrokerInternal(); for(int i = 0; i < descriptor.size(); i++) @@ -752,19 +711,25 @@ CollectionDescriptor col = (CollectionDescriptor) descriptor.get(i); boolean cascadeDelete = getTransaction().cascadeDeleteFor(col); Object collOrArray = col.getPersistentField().get(source.getObject()); + Iterator it = null; if(collOrArray != null) { + CollectionProxy proxy = pb.getProxyFactory().getCollectionProxy(collOrArray); // on delete we have to materialize dependent objects - Iterator it = BrokerHelper.getCollectionIterator(pb, collOrArray); + if(proxy != null) it = proxy.iterator(); + else it = BrokerHelper.getCollectionIterator(pb, collOrArray); + } + if(it != null) + { while(it.hasNext()) { - Object colObj = pb.getProxyFactory().getRealObject(it.next()); + Object colObj = pb.getProxyFactory().getRealObject(it.next()); Identity oid = pb.serviceIdentity().buildIdentity(colObj); ObjectEnvelope colMod = get(oid, colObj, false); if(cascadeDelete) { colMod.setModificationState(colMod.getModificationState().markDelete()); - cascadeDeleteFor(colMod, alreadyPrepared); + cascadeDeleteFor(colMod); } else { @@ -783,6 +748,16 @@ } } + void addNewAssociatedIdentity(Identity oid) + { + newAssociatedIdentites.add(oid); + } + + boolean isNewAssociatedObject(Identity oid) + { + return newAssociatedIdentites.contains(oid); + } + void addM2NLinkEntry(CollectionDescriptor cod, Object leftSource, Object rightSource) { if(!cod.isMtoNRelation()) throw new OJBRuntimeException("Expect a m:n releation, but specified a 1:n"); @@ -818,17 +793,27 @@ } /** - * Set the specified identity to the last position in the order list. - * Note: The Identity must already exist in order list. - */ - void moveToLastInOrderList(Identity oid) - { - int index = mvOrderOfIds.indexOf(oid); - // move entry only if exists - if(index > -1 && index < (mvOrderOfIds.size() - 1)) + * Replace the [EMAIL PROTECTED] org.apache.ojb.broker.Identity} + * of a registered [EMAIL PROTECTED] ObjectEnvelope} object. + * + * @param newOid + * @param oldOid + * @return Returns <em>true</em> if successful. + */ + boolean replaceRegisteredIdentity(Identity newOid, Identity oldOid) + { + boolean result = false; + Object oe = objectEnvelopesMap.remove(oldOid); + if(oe != null) + { + objectEnvelopesMap.put(newOid, oe); + result = true; + if(log.isDebugEnabled()) log.debug("Replace identity: " + oldOid + " --replaced-by--> " + newOid); + } + else { - Object id = mvOrderOfIds.remove(index); - mvOrderOfIds.add(id); + log.warn("Can't replace unregistered object identity (" + oldOid + ") with new identity (" + newOid + ")"); } + return result; } }
Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/PBCapsule.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/PBCapsule.java?rev=422235&r1=422234&r2=422235&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/odmg/PBCapsule.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/odmg/PBCapsule.java Sat Jul 15 07:28:03 2006 @@ -1,14 +1,6 @@ package org.apache.ojb.odmg; -import org.apache.ojb.broker.OJBRuntimeException; -import org.apache.ojb.broker.PersistenceBroker; -import org.apache.ojb.broker.PersistenceConfiguration; -import org.apache.ojb.broker.util.logging.Logger; -import org.apache.ojb.broker.util.logging.LoggerFactory; -import org.odmg.ODMGRuntimeException; -import org.odmg.Transaction; - -/* Copyright 2002-2004 The Apache Software Foundation +/* Copyright 2002-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,15 +14,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import org.apache.ojb.broker.OJBRuntimeException; +import org.apache.ojb.broker.PersistenceBroker; +import org.apache.ojb.broker.PersistenceConfiguration; +import org.apache.ojb.broker.util.logging.Logger; +import org.apache.ojb.broker.util.logging.LoggerFactory; +import org.odmg.ODMGRuntimeException; +import org.odmg.Transaction; + +/** + * ONLY VALID FOR READ OPERATIONS! Capsulates the way to obtain + * PersistenceBroker instances when a odmg-tx is running or + * not - Do not forget to call the destroy() method after use. + * When a transaction was found we use the PersistenceBroker instance + * shipped with the Transaction (via [EMAIL PROTECTED] HasBroker}), else we try + * to obtain a broker using the given [EMAIL PROTECTED] PersistenceConfiguration}. + * + * @version $Id$ + */ public final class PBCapsule { - private Logger log = LoggerFactory.getLogger(PBCapsule.class); + private static Logger log = LoggerFactory.getLogger(PBCapsule.class); + private PersistenceBroker broker; private PersistenceConfiguration persistenceConf; private Transaction tx; - private PersistenceBroker broker; private boolean needsTxCommit = false; - private boolean needsPBCommit = false; + private boolean needsClose = false; private boolean isIllegal = false; public PBCapsule(PersistenceConfiguration persistenceConf, Transaction tx) @@ -42,19 +53,13 @@ public PersistenceBroker getBroker() { - if (isIllegal) - { - throw new OJBRuntimeException("You could not reuse PBCapsule after destroy"); - } + if(isIllegal) throw new OJBRuntimeException("You could not reuse PBCapsule after destroy"); return broker; } private void prepare() { - if (isIllegal) - { - throw new OJBRuntimeException("You could not reuse PBCapsule after destroy"); - } + if(isIllegal) throw new OJBRuntimeException("You could not reuse PBCapsule after destroy"); if(tx == null && persistenceConf == null) { throw new ODMGRuntimeException("Invalid constructor arguments, both arguments are 'null'"); @@ -69,12 +74,7 @@ log.debug("No running transaction found, try to get PersistenceBroker instance from configuration " + persistenceConf); } broker = persistenceConf.createPersistenceBroker(); - // begin tx on the PB instance - if (!broker.isInTransaction()) - { - broker.beginTransaction(); - needsPBCommit = true; - } + needsClose = true; } else { @@ -97,20 +97,13 @@ if (log.isDebugEnabled()) log.debug("Indicated to commit tx"); tx.commit(); } - else if (needsPBCommit) + else if (needsClose) { - if (log.isDebugEnabled()) log.debug("Indicated to commit PersistenceBroker"); - try - { - broker.commitTransaction(); - } - finally - { - if (broker != null) broker.close(); - } + if (log.isDebugEnabled()) log.debug("Indicated to close PersistenceBroker"); + if (broker != null) broker.close(); } isIllegal = true; needsTxCommit = false; - needsPBCommit = false; + needsClose = false; } } Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/RuntimeObject.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/RuntimeObject.java?rev=422235&r1=422234&r2=422235&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/odmg/RuntimeObject.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/odmg/RuntimeObject.java Sat Jul 15 07:28:03 2006 @@ -1,11 +1,5 @@ package org.apache.ojb.odmg; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.ojb.broker.Identity; -import org.apache.ojb.broker.PersistenceBrokerInternal; -import org.apache.ojb.broker.core.proxy.IndirectionHandler; -import org.apache.ojb.broker.metadata.ClassDescriptor; - /* Copyright 2002-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +15,12 @@ * limitations under the License. */ +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.ojb.broker.Identity; +import org.apache.ojb.broker.PersistenceBrokerInternal; +import org.apache.ojb.broker.core.proxy.IndirectionHandler; +import org.apache.ojb.broker.metadata.ClassDescriptor; + /** * Helper object encapsulates common used object properties/states, help to reduce * needless metadata calls. @@ -105,33 +105,7 @@ void doIsNewObjectCheck(final TransactionImpl tx) { - /* - detection of new objects is costly (select of ID in DB to check if object - already exists) we do: - a. check if the object has nullified PK field - b. check if the object is already registered - c. lookup from cache and if not found, last option select on DB - */ - final PersistenceBrokerInternal pb = tx.getBrokerInternal(); - boolean isNew = pb.serviceBrokerHelper().hasNullPKField(cld, obj); - if(!isNew) - { - // use method call to guaratee creation of oid - final Identity oid = getIdentity(); - final ObjectEnvelope mod = tx.objectEnvelopeTable.getByIdentity(oid); - if(mod != null) - { - // already registered object, use current state - isNew = mod.needsInsert(); - } - else - { - // if object was found cache, assume it's old - // else make costly check against the DB - isNew = pb.serviceObjectCache().lookup(oid) == null - && !pb.serviceJdbcAccess().doesExist(cld, oid); - } - } + boolean isNew = tx.isTransient(cld, obj, null); this.isNew = isNew ? Boolean.TRUE : Boolean.FALSE; } Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAbortedExceptionOJB.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAbortedExceptionOJB.java?rev=422235&r1=422234&r2=422235&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAbortedExceptionOJB.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAbortedExceptionOJB.java Sat Jul 15 07:28:03 2006 @@ -1,6 +1,6 @@ package org.apache.ojb.odmg; -/* Copyright 2003-2004 The Apache Software Foundation +/* Copyright 2003-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAware.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAware.java?rev=422235&r1=422234&r2=422235&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAware.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionAware.java Sat Jul 15 07:28:03 2006 @@ -1,6 +1,6 @@ package org.apache.ojb.odmg; -/* Copyright 2002-2004 The Apache Software Foundation +/* Copyright 2002-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.io.Serializable; /** + * * TransactionAware is an interface that can be implemented * to provide hooks into the Transaction interface provided * by ObJectRelationalBridge. @@ -60,32 +61,38 @@ */ public interface TransactionAware extends Serializable { - static final long serialVersionUID = 3690863289834166023L; - - /** + static final long serialVersionUID = 3690863289834166023L; /** + * * beforeCommit will give an object a chance to kill a * transaction before it is committed. * To kill a transaction, throw a new TransactionAbortedException. + * */ public void beforeCommit() throws org.odmg.TransactionAbortedException; /** + * * afterCommit is called only after a successful commit has taken * place. + * */ public void afterCommit(); /** + * * beforeAbort is called before a transaction is aborted. + * */ public void beforeAbort(); /** + * * afterAbort will be called after a transaction has been aborted. * The values of fields which get persisted will have changed to * what they were at the begining of the transaction. This method * should be overridden to reset any transient or non-persistent * fields. + * */ public void afterAbort(); } Modified: db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionExt.java URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionExt.java?rev=422235&r1=422234&r2=422235&view=diff ============================================================================== --- db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionExt.java (original) +++ db/ojb/trunk/src/java/org/apache/ojb/odmg/TransactionExt.java Sat Jul 15 07:28:03 2006 @@ -1,9 +1,6 @@ package org.apache.ojb.odmg; -import org.apache.ojb.broker.Identity; -import org.odmg.Transaction; - -/* Copyright 2003-2004 The Apache Software Foundation +/* Copyright 2003-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +15,9 @@ * limitations under the License. */ +import org.odmg.Transaction; +import org.apache.ojb.broker.Identity; + /** * Offers useful none odmg-standard methods of the odmg [EMAIL PROTECTED] org.odmg.Transaction} interface. * <p> @@ -90,7 +90,7 @@ * reference field while this transaction is in use. * * @param target The class to change cascading delete behavior of the references. - * @param referenceField The field name of the 1:1, 1:n or 1:n reference. + * @param referenceField The field name of the 1:1, 1:n or m:n reference. * @param doCascade If <em>true</em> cascading delete is enabled, <em>false</em> disabled. */ public void setCascadingDelete(Class target, String referenceField, boolean doCascade); @@ -126,36 +126,36 @@ */ public void setOrdering(boolean ordering); - /** - * Returns whether or not the persistent method calls determine - * the persistent object order on commit. - * - * @see #setNoteUserOrder(boolean) - */ - public boolean isNoteUserOrder(); - - /** - * If <em>true</em> the order of persisting method calls like - * <br/> - [EMAIL PROTECTED] org.odmg.Transaction#lock(Object, int)}). - * <br/> - [EMAIL PROTECTED] org.odmg.Database#deletePersistent(Object)}). - * <br/> - [EMAIL PROTECTED] org.odmg.Database#makePersistent(Object)}) - * determine the order of objects before commit. - * <br/> - * If <em>false</em> the ordering was determined by OJB's internal - * method calls and user calls. - * <br/> - * However it's possible to set this value as a global property - * for all transactions using [EMAIL PROTECTED] ImplementationExt#setNoteUserOrder(boolean)}. - * <p/> - * <strong>NOTE:</strong> If OJB's ordering algorithm (see - * [EMAIL PROTECTED] #setOrdering(boolean)}) is enabled, the - * order of objects may change on commit. - * - * @param noteUserOrder If <em>true</em> the order of persisting - * method calls determine the order of objects. - * @see ImplementationExt#setNoteUserOrder(boolean) - */ - public void setNoteUserOrder(boolean noteUserOrder); +// /** +// * Returns whether or not the persistent method calls determine +// * the persistent object order on commit. +// * +// * @see #setNoteUserOrder(boolean) +// */ +// public boolean isNoteUserOrder(); +// +// /** +// * If <em>true</em> the order of persisting method calls like +// * <br/> - [EMAIL PROTECTED] org.odmg.Transaction#lock(Object, int)}). +// * <br/> - [EMAIL PROTECTED] org.odmg.Database#deletePersistent(Object)}). +// * <br/> - [EMAIL PROTECTED] org.odmg.Database#makePersistent(Object)}) +// * determine the order of objects before commit. +// * <br/> +// * If <em>false</em> the ordering was determined by OJB's internal +// * method calls and user calls. +// * <br/> +// * However it's possible to set this value as a global property +// * for all transactions using [EMAIL PROTECTED] ImplementationExt#setNoteUserOrder(boolean)}. +// * <p/> +// * <strong>NOTE:</strong> If OJB's ordering algorithm (see +// * [EMAIL PROTECTED] #setOrdering(boolean)}) is enabled, the +// * order of objects may change on commit. +// * +// * @param noteUserOrder If <em>true</em> the order of persisting +// * method calls determine the order of objects. +// * @see ImplementationExt#setNoteUserOrder(boolean) +// */ +// public void setNoteUserOrder(boolean noteUserOrder); /** * Checks if the object with the given [EMAIL PROTECTED] org.apache.ojb.broker.Identity} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
