You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2006/07/15 16:28:05 UTC

svn commit: r422235 [2/3] - in /db/ojb/trunk/src: java/org/apache/ojb/odmg/ java/org/apache/ojb/odmg/oql/ java/org/apache/ojb/odmg/states/ java/org/odmg/ jdori/org/apache/ojb/jdori/sql/ samples/org/apache/ojb/tutorials/

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:mattbaird@yahoo.com">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 {@link 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
+     * {@link #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 {@link #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 {@link 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 {@link 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 {@link org.apache.ojb.broker.Identity}
+     * of a registered {@link 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 {@link HasBroker}), else we try
+ * to obtain a broker using the given {@link 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 {@link 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/> - {@link org.odmg.Transaction#lock(Object, int)}).
-     * <br/> - {@link org.odmg.Database#deletePersistent(Object)}).
-     * <br/> - {@link 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 {@link ImplementationExt#setNoteUserOrder(boolean)}.
-     * <p/>
-     * <strong>NOTE:</strong> If OJB's ordering algorithm (see
-     * {@link #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/> - {@link org.odmg.Transaction#lock(Object, int)}).
+//     * <br/> - {@link org.odmg.Database#deletePersistent(Object)}).
+//     * <br/> - {@link 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 {@link ImplementationExt#setNoteUserOrder(boolean)}.
+//     * <p/>
+//     * <strong>NOTE:</strong> If OJB's ordering algorithm (see
+//     * {@link #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 {@link org.apache.ojb.broker.Identity}



---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org