Package org.apache.ojb.odmg

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

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.
*/
/**
*
* @author <a href="mailto:thma@apache.org">Thomas Mahler</a>
* @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
*
*/

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.core.proxy.IndirectionHandler;
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.FieldDescriptor;
import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
import org.apache.ojb.broker.util.ObjectModification;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.odmg.states.ModificationState;
import org.odmg.ODMGRuntimeException;

/**
* ObjectEnvelope is used during ODMG transactions as a wrapper for a persistent objects declaration
*
*/
public class ObjectEnvelope implements ObjectModification, Serializable
{
    static final long serialVersionUID = -829177767933340522L;
    /**
     * The objects modification state, e.g. Old and Clean
     */
    private ModificationState modificationState = null;
    private Identity oid;
    private Boolean hasChanged;

    /**
     * myObj holds the object we are wrapping.
     */
    private transient Object myObj;

    /**
     * beforeImage holds a mapping between field
     * names and values at the start of the transaction.
     * afterImage holds the mapping at the
     * end of the transaction.
     */
    private transient Map beforeImage;
    private transient TransactionImpl tx;

    /**
     *
     * Create a wrapper by providing an Object.
     */
    public ObjectEnvelope(Object obj, TransactionImpl aTx)
    {
        tx = aTx;
        myObj = ProxyHelper.getRealObject(obj);
        refreshObjectImage();
        prepareInitialState();
    }

    public void close()
    {
        myObj = null;
        beforeImage = null;
        tx = null;
    }

    public void refreshObjectImage()
    {
        try
        {
            hasChanged = null;
            oid = new Identity(myObj, tx.getBroker());
            beforeImage = getMap();
        }
        catch(Exception ex)
        {
            beforeImage = null;
            oid = null;
            throw new org.odmg.ClassNotPersistenceCapableException(ex.toString());
        }
    }

    public Identity getIdentity()
    {
        return oid;
    }

    /**
     * returns the managed object.
     */
    public Object getObject()
    {
        return myObj;
    }

    /**
     * We need to implement the Two-Phase Commit
     * protocol.
     *
     * beginCommit is where we say if we can or cannot
     * commit the transaction.  At the begining however,
     * we need to attain the after image so we can isolate
     * everything.
     *
     * We should issue the call against the database
     * at this point.  If we get a SQL Exception, we
     * should throw the org.odmg.TransactionAbortedException.
     *
     * We should also check to see if the object is
     * TransactionAware.  If so, we should give it a chance
     * to kill the transaction before we toss it to the
     * database.
     */
    public void beforeCommit()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.beforeCommit();
        }
    }

    /**
     * Method declaration
     */
    public void afterCommit()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.afterCommit();
        }
    }

    /**
     * Method declaration
     */
    public void beforeAbort()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.beforeAbort();
        }
    }

    /**
     * Method declaration
     */
    public void afterAbort()
    {
        if(myObj instanceof TransactionAware)
        {
            TransactionAware ta = (TransactionAware) myObj;
            ta.afterAbort();
        }
    }

    /**
     * getMap() will return the image of the Object.
     */
    private Map getMap() throws PersistenceBrokerException
    {
        Map fieldValues = new HashMap();
        ClassDescriptor mif = tx.getBroker().getClassDescriptor(getObject().getClass());
        /**
         * MBAIRD
         * 1. register all fields of object that aren't collections or references
         */
        FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
        for(int i = 0; i < fieldDescs.length; i++)
        {
            FieldDescriptor fd = fieldDescs[i];
            PersistentField f = fd.getPersistentField();
            fieldValues.put(fd.getColumnName(), f.get(myObj));
        }
        /**
         * MBAIRD
         * 2. register all 1:1 references
         * field changes to 1:1 mapped objects should also be registered in the map,
         * so that alterations to those related objects will trigger an object to be
         * marked "dirty", otherwise attaching or detaching a 1:1 referenced object will
         * not be updated in ODMG.
         */
        Iterator iter = mif.getObjectReferenceDescriptors().iterator();
        ObjectReferenceDescriptor rds = null;
        Object temp = null;
        while(iter.hasNext())
        {
            rds = (ObjectReferenceDescriptor) iter.next();

/*
             * synchronize on myObj so the ODMG-layer can take a snapshot only of
             * fully cached (i.e. with all references + collections) objects
             */
            synchronized(myObj)
            {
                temp = rds.getPersistentField().get(myObj);
            }
            /**
             * MBAIRD
             * In the case of a proxy, we check if it has been materialized
             * if it's been materialized, we put it in the map, because it could change.
             * if it hasn't been materialized, it hasn't changed.
             *
             * Also handles virtual proxies.
             */
            IndirectionHandler handler = ProxyHelper.getIndirectionHandler(temp);

            if(handler != null)
            {
                /**
                 * only register if the proxy has been materialized
                 * if it's materialized later and the map is compared, it will
                 * trigger the update
                 */
                fieldValues.put(rds, handler.getIdentity());
            }
            else
            {
                fieldValues.put(rds, temp);
            }
        }
        /**
         * MBAIRD
         * 3. now let's register the collection descriptors
         * How do we handle proxied collections and collections of proxies
         * @see org.apache.ojb.odmg.OneToManyTest
         */
        Iterator collections = mif.getCollectionDescriptors().iterator();
        CollectionDescriptor collectionDescriptor = null;
        while(collections.hasNext())
        {
            collectionDescriptor = (CollectionDescriptor) collections.next();
            Object collectionOrArray = collectionDescriptor.getPersistentField().get(myObj);
            if(collectionOrArray != null)
            {
                int referencesId = 0;
                // no special treatment for CollectionProxies required,
                // their size() method does not materialize the elements.
                /**
                 * MBAIRD
                 * size isn't the safest thing to use as the dirty bit. This will
                 * need to be fixed.
                 */
                if(collectionOrArray instanceof Collection)
                {
                    referencesId = ((Collection) collectionOrArray).size();
                }
                else if(collectionOrArray.getClass().isArray())
                {
                    referencesId = Array.getLength(collectionOrArray);
                }
                else
                {
                    referencesId = collectionOrArray.hashCode();
                }

                fieldValues.put(collectionDescriptor, new Integer(referencesId));
            }
        }
        return fieldValues;
    }

    /**
     * returns the Modification-state.
     * @return org.apache.ojb.server.states.ModificationState
     */
    public ModificationState getModificationState()
    {
        return modificationState;
    }

    /**
     * returns true if the underlying Object needs an INSERT statement, else returns false.
     */
    public boolean needsInsert()
    {
        return this.getModificationState().needsInsert();
    }

    /**
     * returns true if the underlying Object needs an UPDATE statement, else returns false.
     */
    public boolean needsUpdate()
    {
        return this.getModificationState().needsUpdate();
    }

    /**
     * returns true if the underlying Object needs an UPDATE statement, else returns false.
     */
    public boolean needsDelete()
    {
        return this.getModificationState().needsDelete();
    }

    /**
     * sets the initial MoificationState of the wrapped object myObj. The initial state will be StateNewDirty if myObj
     * is not persisten already. The state will be set to StateOldClean if the object is already persistent.
     */
    private void prepareInitialState()
    {
        // determine appropriate modification state
        ModificationState initialState = null;
        boolean needsInsert;
        try
        {
            // try to lookup the object.
            needsInsert = tx.getBroker().serviceObjectCache().lookup(oid) == null
                    && !tx.getBroker().serviceBrokerHelper().doesExist(
                    tx.getBroker().getClassDescriptor(myObj.getClass()), oid, myObj);
        }
        catch(PersistenceBrokerException ex)
        {
            LoggerFactory.getDefaultLogger().error("ObjectEnvelope: ", ex);
            throw new ODMGRuntimeException("Unexpected error while check existence of "
                    + myObj + ", exception was "+ ex);
        }
        if(needsInsert)
        {
            // if object is not already persistent it must be marked as new
            // it must be marked as dirty because it must be stored even if it will not modified during tx
            initialState = org.apache.ojb.odmg.states.StateNewDirty.getInstance();
        }
        else if(tx.isDeleted(oid))
        {
            // if object is already persistent it will be marked as old.
            // it is marked as dirty as it has been deleted during tx and now it is inserted again,
            // possibly with new field values.
            initialState = org.apache.ojb.odmg.states.StateOldDirty.getInstance();
        }
        else
        {
            // if object is already persistent it will be marked as old.
            // it is marked as clean as it has not been modified during tx already
            initialState = org.apache.ojb.odmg.states.StateOldClean.getInstance();
        }
        // remember it:
        modificationState = initialState;
    }

    /**
     * set the Modification state to a new value. Used during state transitions.
     * @param newModificationState org.apache.ojb.server.states.ModificationState
     */
    public void setModificationState(ModificationState newModificationState)
    {
        if(newModificationState != modificationState)
        {
            if(LoggerFactory.getDefaultLogger().isDebugEnabled())
            {
                LoggerFactory.getDefaultLogger().debug("transition: " + new Identity(this.myObj, tx.getBroker()) + " ("
                        + modificationState + " --> " + newModificationState + ")");
            }
            modificationState = newModificationState;
        }
    }

    /**
     * returns a String representation.
     * @return java.lang.String
     */
    public String toString()
    {
        String obj = null;
        if(myObj == null)
        {
            obj = "null";
        }
        else
        {
            obj = new Identity(myObj, tx.getBroker()).toString();
        }
        return obj + "(" + modificationState + ")";
    }

    /**
     * checks whether object and internal clone differ and returns true if so, returns false else.
     * @return boolean
     */
    public boolean hasChanged()
    {
        if(hasChanged == null)
        {
            Map currentImage = null;
            try
            {
                currentImage = getMap();
            }
            catch(Exception e)
            {
                LoggerFactory.getDefaultLogger().warn("Could not verify object changes, return hasChanged 'true'", e);
            }

            hasChanged = (beforeImage != null && beforeImage.equals(currentImage) ? Boolean.FALSE : Boolean.TRUE);
        }
        return hasChanged.booleanValue();
    }

/*
arminw: we can't really restore object state with all dependencies and fields
without having a deep copy of the clean object. To avoid side-effects disable this
feature
*/

//    public void rollback()
//    {
//      if(myObj != null && beforeImage != null)
//      {
//      ClassDescriptor mif = tx.getBroker().getClassDescriptor(getObject().getClass());
//      /**
//       * Set all the field values back.
//       */
//      FieldDescriptor[] fieldDescs = mif.getFieldDescriptions();
//      for (int i = 0; i < fieldDescs.length; i++)
//      {
//         FieldDescriptor fd = fieldDescs[i];
//         PersistentField f = fd.getPersistentField();
//
//         Object value = beforeImage.get(fd.getColumnName());
//
//         f.set(myObj, value);
//      }
//
//      /**
//       * Set all the Object Reference values back
//       * Don't know exactly what to do when the are identiy objects
//       * (VirtualProxy or proxy classes)
//       */
//      Iterator iter = mif.getObjectReferenceDescriptors().iterator();
//      while (iter.hasNext())
//      {
//        ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) iter.next();
//         Object temp = beforeImage.get(rds);
//
//         if(temp instanceof Identity)
//        {
//          // TODO what to do now? It was a VirtualProxy instance or a Proxy class.
//        }
//        else
//        {
//          rds.getPersistentField().set(myObj, temp);
//        }
//      }
//
//      /**
//       * Now set all collections back. But we only stored DirtyMarks.
//       * So what can be rolledback.?
//       */
//      // Iterator collections = mif.getCollectionDescriptors().iterator();
//      }
//    }
}
TOP

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

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.