Package org.apache.ojb.odmg

Source Code of org.apache.ojb.odmg.ObjectEnvelopeTable

package org.apache.ojb.odmg;

/* Copyright 2002-2004 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.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.OptimisticLockException;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerFactory;
import org.apache.ojb.broker.accesslayer.ConnectionManagerIF;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
import org.apache.ojb.broker.util.BrokerHelper;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.odmg.locking.LockManagerFactory;
import org.apache.ojb.odmg.states.StateOldClean;
import org.odmg.LockNotGrantedException;
import org.odmg.Transaction;
import org.odmg.TransactionAbortedException;

/**
* 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
{
    private Logger log = LoggerFactory.getLogger(ObjectEnvelopeTable.class);
    private TransactionImpl transaction;

    /**
     * the internal table mapping Objects to their ObjectTransactionWrappers
     */
    private Map mhtObjectEnvelopes = new HashMap();

    /**
     * a vector 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.
     */
    private ArrayList mvOrderOfIds = new ArrayList();

    /**
     * marker used to avoid superfluous reordering and commiting
     */
    private boolean needsCommit = false;

    /**
     * prepare this instance for reuse
     */
    public void refresh()
    {
        /**
         * MBAIRD: be nice and remove all references so they can be
         * gc'd
         */
        if (mhtObjectEnvelopes != null)
        {
            Iterator iter = mvOrderOfIds.iterator();
            ObjectEnvelope temp;
            while (iter.hasNext())
            {
                temp = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
                temp.close();
            }
        }
        needsCommit = false;
        mhtObjectEnvelopes.clear();
        mvOrderOfIds.clear();
    }

    /**
     * Creates new ObjectEnvelopeTable
     */
    public ObjectEnvelopeTable(TransactionImpl myTransaction)
    {
        transaction = myTransaction;
    }

    /**
     * perform commit on all tx-states
     */
    public void commit() throws TransactionAbortedException, LockNotGrantedException
    {
        PersistenceBroker broker = transaction.getBroker();
        ConnectionManagerIF connMan = broker.serviceConnectionManager();
        boolean saveBatchMode = connMan.isBatchMode();

        try
        {
            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())
            {
//                if (log.isDebugEnabled())
//                    log.debug("call beginTransaction() on PB instance");
//                broker.beginTransaction();
                log.error("PB associated with current odmg-tx is not in tx");
                throw new TransactionAbortedException("Underlying PB is not in tx");
            }

            // Committing has to be done in two phases. First implicitly upgrade to lock on all related
            // objects of objects in this transaction. Then the list of locked objects has to be
            // reordered to solve referential integrity dependencies, then the objects are
            // written into the database.

            // 0. turn on the batch mode
            connMan.setBatchMode(true);

            // 1. upgrade implicit locks.
            upgradeImplicitLocksAndCheckIfCommitIsNeeded();

            // 2. Reorder objects
            reorder();

            // 3. commit objects.
            commitAllEnvelopes(broker);

            // 4. execute batch
            connMan.executeBatch();

            // 5.Update all Envelopes to new CleanState
            setCleanState();

        }
        catch (Throwable t)
        {
            // we do that in TransactionImpl#abort()
            //            broker.abortTransaction();
            connMan.clearBatch();
            log.error("Commit on object level failed for tx " + transaction, t);
            if (t instanceof OptimisticLockException)
            {
              // PB OptimisticLockException should be clearly signalled to the user
              throw new LockNotGrantedException(t.getMessage());
            }
            else
            {
                    throw new TransactionAbortedExceptionOJB(t);
            }
        }
        finally
        {
            needsCommit = false;
            connMan.setBatchMode(saveBatchMode);
        }
    }

    /**
     * commit all envelopes against the current broker
     * @param broker the PB to persist all objects
     */
    private void commitAllEnvelopes(PersistenceBroker broker)
    {
        if (needsCommit)
        {
            Iterator iter;
            // using clone to avoid ConcurentModificationException
            iter = ((List) mvOrderOfIds.clone()).iterator();
            while (iter.hasNext())
            {
                ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
                mod.getModificationState().commit(mod, broker);
            }
        }
    }

  /**
   *
   */
  private void setCleanState()
  {
     if (needsCommit)
     {
        Iterator iter;
        // using clone to avoid ConcurentModificationException
        iter = ((List) mvOrderOfIds.clone()).iterator();
        while (iter.hasNext())
        {
          ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
          if(mod.getModificationState() != StateOldClean.getInstance())
          {
            mod.refreshObjectImage();
            mod.setModificationState(StateOldClean.getInstance());
          }
        }
     }
  }

    /**
     * Implicitely upgrade locks on modified objects.
     * Also checks if there are any operations to commit.
     */
    private void upgradeImplicitLocksAndCheckIfCommitIsNeeded()
    {
        boolean useImplicitLocking = getConfiguration().useImplicitLocking();
        // using clone to avoid ConcurentModificationException
        Iterator iter = ((List) mvOrderOfIds.clone()).iterator();
        while (iter.hasNext())
        {
      boolean markDirty = false;
            ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
            // if the Object has been modified by transaction, mark object as dirty
            // but only if it has not been marked during tx already !!
            if ((!mod.needsDelete()) && (!mod.needsInsert()) && (!mod.needsUpdate()))
            {
        /**
         * second check is, has the object in the envelope changed.
         */
                if (mod.hasChanged())
                {
          /**
           * now, the quickest thing to check is the useImplicitLocking flag. If we are using
           * implicit locking, let's try to upgrade the lock, and mark the markDirty
           */
          if (useImplicitLocking)
          {
            // implicitely acquire a write lock !
            transaction.lock(mod.getObject(), Transaction.UPGRADE);
            // objects needs commit action, thus set markDirty to true:
            markDirty = true;
          }
          /**
           * If useImplicitLocking is false, we still need to check if the object in the envelope
           * is write locked. If it is, we don't have to upgrade the lock, just mark markDirty
           */
          else if (LockManagerFactory.getLockManager().checkWrite(transaction, mod.getObject()))
          {
            // objects needs commit action, thus set markDirty to true:
            markDirty = true;
          }
          if (markDirty)
          {
            needsCommit=true;
            // mark object dirty
            mod.setModificationState(mod.getModificationState().markDirty());
          }
                }
            }
            else
            {
                // objects needs commit action, thus set needCommit to true:
                needsCommit = true;
            }
        }
    }

    /**
     * perform rollback on all tx-states
     */
    public void rollback()
    {
        try
        {
            PersistenceBroker broker = transaction.getBroker();
            Iterator iter = mvOrderOfIds.iterator();
            while (iter.hasNext())
            {
                ObjectEnvelope mod = (ObjectEnvelope) mhtObjectEnvelopes.get(iter.next());
                if (log.isDebugEnabled())
                    log.debug("rollback: " + mod);
                // if the Object has been modified has been modified by transaction, mark object as dirty
                if (mod.hasChanged())
                {
                    mod.setModificationState(mod.getModificationState().markDirty());
                }
                mod.getModificationState().rollback(mod, broker);
            }
        }
        finally
        {
            needsCommit = false;
        }
    }

    /**
     * remove an objects entry from the Hashtable
     */
    public void remove(Object pKey)
    {
        Identity id = null;
        if (pKey instanceof Identity)
        {
            id = (Identity) pKey;
        }
        else
        {
            id = new Identity(pKey, transaction.getBroker());
        }
        mhtObjectEnvelopes.remove(id);
        mvOrderOfIds.remove(id);
    }

    /**
     *
     * Get an enumeration of all the elements in this ObjectEnvelopeTable
     * in random order.
     *
     * Creation date: (11.02.2001 12:45:08)
     *
     * @return Enumeration an enumeration of all elements managed by this ObjectEnvelopeTable
     *
     */
    public Enumeration elements()
    {
        return java.util.Collections.enumeration(mhtObjectEnvelopes.values());
    }

    /**
     * retrieve an objects ObjectModification state from the hashtable
     */
    public ObjectEnvelope getByIdentity(Identity id)
    {
        return (ObjectEnvelope) mhtObjectEnvelopes.get(id);
    }

    /**
     * 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)
    {
        Identity id = new Identity(pKey, transaction.getBroker());
        //Integer keyInteger = new Integer(System.identityHashCode(key));
        ObjectEnvelope result = (ObjectEnvelope) mhtObjectEnvelopes.get(id);
        if (result == null)
        {
            result = new ObjectEnvelope(pKey, transaction);
            mhtObjectEnvelopes.put(id, result);
            mvOrderOfIds.add(id);
            if (log.isDebugEnabled())
                log.debug("register: " + result);
        }
        return result;
    }

    /**
     * store an objects transactional state into the Hashtable
     */
    public void put(Object pKey, ObjectEnvelope modification)
    {
        Identity id = new Identity(pKey, transaction.getBroker());
        //Integer keyInt = new Integer(System.identityHashCode(key));
        if (log.isDebugEnabled())
            log.debug("register: " + modification);
        if (!mhtObjectEnvelopes.containsKey(id))
            mvOrderOfIds.add(id);
        mhtObjectEnvelopes.put(id, modification);
    }

    /**
     * Returns a String representation of this object
     */
    public String toString()
    {
        ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
        buf.append("### ObjectEnvelopeTable dump:");
        Enumeration enum = elements();
        while (enum.hasMoreElements())
        {
            ObjectEnvelope mod = (ObjectEnvelope) enum.nextElement();
            buf.append(mod.toString());
        }
        return buf.toString();
    }

    /**
     * retrieve an objects ObjectModification state from the hashtable
     */
    public boolean contains(Object pKey)
    {
        Identity id = new Identity(pKey, transaction.getBroker());
        //Integer keyInteger = new Integer(System.identityHashCode(key));
        return mhtObjectEnvelopes.containsKey(id);
    }

    /**
     * Reorder the objects in the table to resolve referential integrity dependencies.
     */
    private void reorder() throws IllegalAccessException
    {
        if (needsCommit)
        {
            ArrayList vNewVector = new ArrayList(mvOrderOfIds.size());
            Map htNewHashtable = new HashMap((int) (mvOrderOfIds.size() * 1.1), 1f);
            Map htOldVectorPosition = new HashMap((int) (mvOrderOfIds.size() * 1.1), 1f);
            for (int i = 0; i < mvOrderOfIds.size(); i++)
                htOldVectorPosition.put(mvOrderOfIds.get(i), new Integer(i));
            for (int i = 0; i < mvOrderOfIds.size(); i++)
            {
                Identity id = (Identity) mvOrderOfIds.get(i);
                if (id != null)
                {
                    mvOrderOfIds.set(i, null);
                    ObjectEnvelope o = (ObjectEnvelope) mhtObjectEnvelopes.get(id);
                    mhtObjectEnvelopes.remove(id);
                    reorderObject(htNewHashtable, vNewVector, o, id, htOldVectorPosition);
                }
            }
            mvOrderOfIds = vNewVector;
            mhtObjectEnvelopes = htNewHashtable;
        }
    }

    /**
     * put an object and all its dependent objects in the new vector. If the object
     * in question is going to be DELETEd, first the objects referenced in collections
     * are put in the vector, then the object in question and then the references.
     * Otherwise the order is reversed.
     */
    private void reorderObject(
        Map htNewHashtable,
        List newVector,
        ObjectEnvelope objectToReorder,
        Identity id,
        Map htOldVectorPosition)
        throws IllegalAccessException
    {
        PersistenceBroker broker = transaction.getBroker();
        if (objectToReorder != null)
        {
            ClassDescriptor cld = broker.getClassDescriptor(objectToReorder.getObject().getClass());
            if (objectToReorder.needsDelete())
            {
                reorderCollection(
                    htNewHashtable,
                    newVector,
                    objectToReorder,
                    cld,
                    htOldVectorPosition);
                newVector.add(id);
                htNewHashtable.put(id, objectToReorder);
                reorderReference(
                    htNewHashtable,
                    newVector,
                    objectToReorder,
                    cld,
                    htOldVectorPosition);
            }
            else
            {
                reorderReference(
                    htNewHashtable,
                    newVector,
                    objectToReorder,
                    cld,
                    htOldVectorPosition);
                newVector.add(id);
                htNewHashtable.put(id, objectToReorder);
                reorderCollection(
                    htNewHashtable,
                    newVector,
                    objectToReorder,
                    cld,
                    htOldVectorPosition);
            }
        }
    }

    /**
     * Resolves all objects referenced in collections of objectToReorder. The referenced
     * objects are removed from the list of modified objects in this class and passed
     * to reorderObject, where dependencies of the object are resolved (i.e. if the referenced
     * object contains again collections or references) and it is finally put into the new order
     * vector.
     */
    private void reorderCollection(
        Map htNewHashtable,
        List newVector,
        ObjectEnvelope objectToReorder,
        ClassDescriptor cld,
        Map htOldVectorPosition)
        throws IllegalAccessException
    {
        Iterator i;
        i = cld.getCollectionDescriptors().iterator();
        while (i.hasNext())
        {
            CollectionDescriptor cds = (CollectionDescriptor) i.next();
            Object col = cds.getPersistentField().get(objectToReorder.getObject());
            if (col != null)
            {
                Iterator colIterator;
                if (ProxyHelper.isCollectionProxy(col) && !ProxyHelper.getCollectionProxy(col).isLoaded())
                {
                    colIterator = Collections.EMPTY_LIST.iterator();
                }
                else
                {
                    colIterator = BrokerHelper.getCollectionIterator(col);
                }
                while (colIterator.hasNext())
                {
                    // The collection now contains all the objects in the collection.
                    // Now we have to retrieve the ObjectEnvelope representing this
                    // Object from the hashtable, remove it from the vector and reorder
                    // the retrieved ObjectEnvelope.
                    Identity id = new Identity(colIterator.next(), transaction.getBroker());
                    ObjectEnvelope oe = (ObjectEnvelope) mhtObjectEnvelopes.get(id);
                    if (oe != null)
                    {
                        mvOrderOfIds.set(((Integer) htOldVectorPosition.get(id)).intValue(), null);
                        // mvOrderOfIds.set(mvOrderOfIds.indexOf(id), null);
                        mhtObjectEnvelopes.remove(id);
                        reorderObject(htNewHashtable, newVector, oe, id, htOldVectorPosition);
                    }
                }
            }
        }
    }

    /**
     * Resolves all objects referenced in references of objectToReorder. The referenced
     * objects are removed from the list of modified objects in this class and passed
     * to reorderObject, where dependencies of the object are resolved (i.e. if the referenced
     * object contains again collections or references) and it is finally put into the new order
     * vector.
     */
    private void reorderReference(
        Map htNewHashtable,
        List newVector,
        ObjectEnvelope objectToReorder,
        ClassDescriptor cld,
        Map htOldVectorPosition)
        throws IllegalAccessException
    {
        Iterator i = cld.getObjectReferenceDescriptors().iterator();
        while (i.hasNext())
        {
            ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i.next();
            Object refObj = rds.getPersistentField().get(objectToReorder.getObject());
            if (refObj != null)
            {
                Identity id = new Identity(refObj, transaction.getBroker());
                ObjectEnvelope oe = (ObjectEnvelope) mhtObjectEnvelopes.get(id);
                if (oe != null)
                {
                    mhtObjectEnvelopes.remove(id);
                    mvOrderOfIds.set(((Integer) htOldVectorPosition.get(id)).intValue(), null);
                    // mvOrderOfIds.set(mvOrderOfIds.indexOf(id), null);
                    reorderObject(htNewHashtable, newVector, oe, id, htOldVectorPosition);
                }
            }
        }
    }

    /**
     * get Configuration
     * @return OdmgConfiguration
     */
    private OdmgConfiguration getConfiguration()
    {
        OdmgConfiguration config =
            (OdmgConfiguration) PersistenceBrokerFactory.getConfigurator().getConfigurationFor(
                null);
        return config;
    }

}
TOP

Related Classes of org.apache.ojb.odmg.ObjectEnvelopeTable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.