Package org.apache.ojb.broker

Source Code of org.apache.ojb.broker.Identity

package org.apache.ojb.broker;

/* 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 org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
import org.apache.ojb.broker.core.ValueContainer;
import org.apache.ojb.broker.core.proxy.IndirectionHandler;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.util.BrokerHelper;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.ArrayUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
* Represents the identity of an object.
* <br/>
* It's composed of:
* <ul>
* <li>
* class of the real object
* </li>
* <li>
* top-level class of the real object (could be an abstract class or interface or the
* class of the object itself), used to make an object unique across extent classes
* </li>
* <li>
* an array of all primary key value objects
* </li>
* </ul>
* <p>
* If in the metadata of an persistent capable object class the attribute <em>autoincrement</em>
* is set true, new primary key values will be automatic assigned to the given object passed as
* constructor argument.
* </p>
* <p>
* NOTE: An <em>Identity</em> object must be unique
* accross extents. Means all objects with the same top-level class need unique
* PK values.
* </p>
* @see org.apache.ojb.broker.IdentityFactory

* @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
* @version $Id: Identity.java,v 1.36.2.8 2005/03/23 12:39:57 arminw Exp $
*/
public class Identity implements Serializable
{
  static final long serialVersionUID = 3182285550574178710L;

    /**
     * the top-level Class of the identified object<br>
     * ie: an Interface
     */
    private Class m_objectsTopLevelClass;

    /**
     * the real Class of the identified object<br>
     * ie: the implementing Class
     */
    private Class m_objectsRealClass = null;

    /**
     * The ordered list of primary key values maintaining the objects identity in the underlying RDBMS
     */
    private Object[] m_pkValues;

    /*
    In distributed enviroments the Identity object have to recalculate the
    hashCode and toString values, because the hash code of the Class object
    differs in different JVM
    */
    private transient String m_stringRepresentation = null;
    private transient Integer m_hashCode;

    /**
     * creates an Identity from a class and the objects primary key values.
     * used for the definition of proxies.
     *
     * @param realClass the concrete class of the object, or null if not known.
     * @param topLevel the highest persistence-capable class or
     * interface (in the inheritance hierarchy) that the identified object is an instance of
     * @param pkValues (unique across the extents !)
    */
    public Identity(final Class realClass, final Class topLevel, final Object[] pkValues)
    {
        m_objectsTopLevelClass = topLevel;
        m_objectsRealClass = realClass;
        m_pkValues = pkValues;
        checkForPrimaryKeys(null);
    }

    public Identity(final Object objectToIdentitify, final PersistenceBroker targetBroker)
    {
        init(objectToIdentitify, targetBroker, null);
    }

    public Identity(final Object objectToIdentitify, final PersistenceBroker targetBroker, final ClassDescriptor cld)
    {
        init(objectToIdentitify, targetBroker, cld);
    }

    private void init(final Object objectToIdentify, final PersistenceBroker targetBroker, ClassDescriptor cld)
    {
        if(objectToIdentify == null) throw new OJBRuntimeException("Can't create Identity for 'null'-object");
        try
        {
            final IndirectionHandler handler = ProxyHelper.getIndirectionHandler(objectToIdentify);

            synchronized(objectToIdentify)
            {
                if (handler != null)
                {
                    final Identity sourceOID = handler.getIdentity();
                    m_objectsTopLevelClass = sourceOID.m_objectsTopLevelClass;
                    m_objectsRealClass = sourceOID.m_objectsRealClass;
                    m_pkValues = sourceOID.m_pkValues;
                }
                else
                {
                    if (cld == null)
                    {
                        cld = targetBroker.getClassDescriptor(objectToIdentify.getClass());
                    }

                    // identities must be unique accross extents !
                    m_objectsTopLevelClass = targetBroker.getTopLevelClass(objectToIdentify.getClass());
                    m_objectsRealClass = objectToIdentify.getClass();

                    // BRJ: definitely do NOT convertToSql
                    // conversion is done when binding the sql-statement
                    final BrokerHelper helper = targetBroker.serviceBrokerHelper();
                    final ValueContainer[] pkValues = helper.getKeyValues(cld, objectToIdentify, false);
                    if (pkValues == null || pkValues.length == 0)
                    {
                        throw createException("Can't extract PK value fields", objectToIdentify, null);
                    }
                    m_pkValues = helper.extractValueArray(pkValues);
                }
            }

            checkForPrimaryKeys(objectToIdentify);
        }
        catch (ClassNotPersistenceCapableException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw createException("Can not init Identity for given object.", objectToIdentify, e);
        }
    }

    /**
     * Factory method that returns an Identity object from the given
     * byte array - see {@link #serialize}.
     */
    public static Identity fromByteArray(final byte[] anArray) throws PersistenceBrokerException
    {
        // reverse of the serialize() algorithm:
        // read from byte[] with a ByteArrayInputStream, decompress with
        // a GZIPInputStream and then deserialize by reading from the ObjectInputStream
        try
        {
            final ByteArrayInputStream bais = new ByteArrayInputStream(anArray);
            final GZIPInputStream gis = new GZIPInputStream(bais);
            final ObjectInputStream ois = new ObjectInputStream(gis);
            final Identity result = (Identity) ois.readObject();
            ois.close();
            gis.close();
            bais.close();
            return result;
        }
        catch (Exception ex)
        {
            throw new PersistenceBrokerException(ex);
        }
    }

    /**
     * Return the top-level class of the real
     * subject (means class name of a base class,
     * base interface denoted in the repository or
     * objects real class name if none top-level was found)
     *
     */
    public Class getObjectsTopLevelClass()
    {
        return m_objectsTopLevelClass;
    }

    /**
     * Return the "real" Class of the real subject
     */
    public Class getObjectsRealClass()
    {
        return m_objectsRealClass;
    }

    /**
     * Set the objects real class
     */
    public void setObjectsRealClass(final Class objectsRealClass)
    {
        this.m_objectsRealClass = objectsRealClass;
    }

    /**
     * Return a serialized Identity as byte[].
     * @see #fromByteArray
     */
    public byte[] serialize() throws PersistenceBrokerException
    {
        // Identity is serialized and written to an ObjectOutputStream
        // This ObjectOutputstream is compressed by a GZIPOutputStream
        // and finally written to a ByteArrayOutputStream.
        // the resulting byte[] is returned
        try
        {
            final ByteArrayOutputStream bao = new ByteArrayOutputStream();
            final GZIPOutputStream gos = new GZIPOutputStream(bao);
            final ObjectOutputStream oos = new ObjectOutputStream(gos);
            oos.writeObject(this);
            oos.close();
            gos.close();
            bao.close();
            final byte[] result = bao.toByteArray();
            return result;
        }
        catch (Exception ignored)
        {
            throw new PersistenceBrokerException(ignored);
        }
    }

    /**
     * return a String representation.
     * @return java.lang.String
     */
    public String toString()
    {
        if (m_stringRepresentation == null)
        {
            final StringBuffer buf = new StringBuffer();
            buf.append(m_objectsTopLevelClass.getName());
            for (int i = 0; i < m_pkValues.length; i++)
            {
                buf.append((i == 0) ? "{" : ",");
                buf.append(m_pkValues[i]);
            }
            buf.append("}");
            m_stringRepresentation = buf.toString();
        }
        return m_stringRepresentation;
    }


    /**
     * OJB can handle only classes that declare at least one primary key attribute,
     * this method checks this condition.
     * @exception ClassNotPersistenceCapableException thrown if no primary key is
     * specified for the objects class
     */
    protected void checkForPrimaryKeys(final Object realObject) throws ClassNotPersistenceCapableException
    {
        // if no PKs are specified OJB can't handle this class !
        if (m_pkValues == null || m_pkValues.length == 0)
        {
            throw createException("OJB needs at least one primary key attribute for class: ", realObject, null);

        }
        if(m_pkValues[0] instanceof ValueContainer)
            throw new OJBRuntimeException("Can't handle pk values of type "+ValueContainer.class.getName());
    }

    /**
     * return the list of Primary Key Values of the real subject
     * @return Object[]
     */
    public Object[] getPrimaryKeyValues()
    {
        return m_pkValues;
    }

    /**
     * Compare this Identity object to any other object. This comparison is delegated
     * to the super-class.
     */
    public boolean equals(final Object obj)
    {
        if(this == obj) return true;

        boolean result = false;
        if (obj instanceof org.apache.ojb.broker.Identity)
        {
            final Identity id = (Identity) obj;
            final Object[] otherPkValues = id.getPrimaryKeyValues();

            result = getObjectsTopLevelClass().equals(id.getObjectsTopLevelClass())
                    && (m_pkValues.length == otherPkValues.length);
            for (int i = 0; result && i < m_pkValues.length; i++)
            {
                result = (m_pkValues[i] == null) ? (otherPkValues[i] == null)
                        : m_pkValues[i].equals(otherPkValues[i]);

                // special treatment for byte[]
                if (!result && m_pkValues[i] instanceof byte[] && otherPkValues[i] instanceof byte[])
                {
                    result = Arrays.equals((byte[]) m_pkValues[i], (byte[]) otherPkValues[i]);
                }
            }
        }
        return result;
    }

    /**
     * Calculate a hashcode for this Identity. The Hashcode should be
     * equal for Identities where Identity.equals() returns true. Therefore
     * all primary key values that return a hashcode depending on its content
     * (this is assumed for String and Number) are taken into account. Additionally
     * the hashCode() of the top-level class name of this Identity is representing
     * is taken into account.
     */
    public int hashCode()
    {
        /*
        arminw:
        identity is quasi immutable (toplevel class and PK fields
        never change), thus we can note hashCode
        */
        if(m_hashCode == null)
        {
            final HashCodeBuilder hb = new HashCodeBuilder();
            for (int i = 0; i < m_pkValues.length; i++)
            {
              hb.append(m_pkValues[i]);
            }
            /*
            the hashcode of different objects to be unique across different
            JVM and have to be the same for the same object in different JVM,
            so we can't use
            hb.append(getObjectsTopLevelClass().hashCode());
            because Class identity is not same in different JVM
            */
            hb.append(getObjectsTopLevelClass().getName());
            m_hashCode = new Integer(hb.toHashCode());
        }
        return m_hashCode.intValue();
    }

    private ClassNotPersistenceCapableException createException(String msg, final Object objectToIdentify, final Exception e)
    {
        final String eol = SystemUtils.LINE_SEPARATOR;
        if(msg == null)
        {
            msg = "Unexpected error:";
        }
        if(e != null)
        {
            return new ClassNotPersistenceCapableException(msg + eol +
                        "objectTopLevelClass=" + (m_objectsTopLevelClass != null ? m_objectsTopLevelClass.getName() : null) + eol +
                        "objectRealClass=" + (m_objectsRealClass != null ? m_objectsRealClass.getName() : null) + eol +
                        "pkValues=" + (m_pkValues != null ? ArrayUtils.toString(m_pkValues) : null) +
                        (objectToIdentify != null ? (eol + "object to identify: " + objectToIdentify) : ""), e);
        }
        else
        {
            return new ClassNotPersistenceCapableException(msg + eol +
                        "objectTopLevelClass=" + (m_objectsTopLevelClass != null ? m_objectsTopLevelClass.getName() : null) + eol +
                        "objectRealClass=" + (m_objectsRealClass != null ? m_objectsRealClass.getName() : null) + eol +
                        "pkValues=" + (m_pkValues != null ? ArrayUtils.toString(m_pkValues) : null) +
                        eol + "object to identify: " + objectToIdentify);
        }
    }
}
TOP

Related Classes of org.apache.ojb.broker.Identity

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.