Package org.jpox.jdo

Source Code of org.jpox.jdo.AbstractPersistenceManager

/**********************************************************************
Copyright (c) 2003 David Jencks and others. All rights reserved.
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.

Contributors:
2003 Erik Bengtson - removed exist() operation
2003 Andy Jefferson - commented
2003 Andy Jefferson - added check on makePersistent that class is persistence capable
2003 Andy Jefferson - introduction of localiser
2003 Erik Bengtson - provided initial caching mechanism
2003 Erik Bengtson - added getObjectbyAID
2003 Erik Bengtson - removed unused variables
2004 Erik Bengtson - implemented evictAll   
2004 Andy Jefferson - converted to use Logger
2004 Andy Jefferson - added LifecycleListener
2004 Andy Jefferson - rewritten Cache
2005 Andy Jefferson - moved ClassLoaderResolver handling to separate class
2005 Marco Schulze - implemented copying the lifecycle listeners in j2ee environment
2007 Andy Jefferson - copyOnAttach
     ...
**********************************************************************/
package org.jpox.jdo;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jdo.Extent;
import javax.jdo.FetchPlan;
import javax.jdo.JDOException;
import javax.jdo.JDOFatalUserException;
import javax.jdo.JDOOptimisticVerificationException;
import javax.jdo.JDOUserException;
import javax.jdo.ObjectState;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.datastore.JDOConnection;
import javax.jdo.datastore.Sequence;
import javax.jdo.listener.InstanceLifecycleListener;
import javax.jdo.spi.PersistenceCapable;

import org.jpox.ClassLoaderResolver;
import org.jpox.ObjectManager;
import org.jpox.ObjectManagerImpl;
import org.jpox.exceptions.ClassNotResolvedException;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXOptimisticException;
import org.jpox.exceptions.TransactionActiveOnCloseException;
import org.jpox.identity.SCOID;
import org.jpox.jdo.exceptions.TransactionNotActiveException;
import org.jpox.jdo.exceptions.TransactionNotWritableException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.FetchGroupMetaData;
import org.jpox.metadata.FetchPlanMetaData;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.QueryLanguage;
import org.jpox.metadata.QueryMetaData;
import org.jpox.metadata.SequenceMetaData;
import org.jpox.state.DetachState;
import org.jpox.state.FetchPlanState;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;

/**
* Provide the basics of a JDO PersistenceManager using an underlying ObjectManager to perform the
* actual persistence.
*
* @version $Revision: 1.4 $
*/
public abstract class AbstractPersistenceManager implements javax.jdo.PersistenceManager
{
    /** Localisation utility for output messages from core. */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.Localisation",
        ObjectManager.class.getClassLoader());

    /** Localisation utility for output messages from jdo. */
    protected static final Localiser LOCALISER_JDO = Localiser.getInstance("org.jpox.jdo.Localisation",
        AbstractPersistenceManager.class.getClassLoader());

    /** Map of user objects attached to this PM. */
    private Map userObjectMap;

    /** User object attached to the PM. */
    private Object userObject;

    /** Backing ObjectManager for this PersistenceManager. */
    protected ObjectManager objectMgr;

    protected javax.jdo.Transaction jdotx;

    /** Owning PersistenceManagerFactory. */
    protected AbstractPersistenceManagerFactory apmf;

    /** JDO Fetch Plan. */
    protected JDOFetchPlan fetchPlan = null;

    /**
     * Constructor.
     * @param apmf Persistence Manager Factory
     * @param userName Username for the datastore
     * @param password Password for the datastore
     */
    public AbstractPersistenceManager(AbstractPersistenceManagerFactory apmf, String userName, String password)
    {
        objectMgr = new ObjectManagerImpl(apmf, this, userName, password);
        this.apmf = apmf;
        userObject = null;
        userObjectMap = null;
        fetchPlan = new JDOFetchPlan(objectMgr.getFetchPlan());
    }

    /**
     * Convenience accessor for the ObjectManager performing the actual persistence.
     * @return The ObjectManager
     */
    public ObjectManager getObjectManager()
    {
        return objectMgr;
    }

    /**
     * Accessor for the PersistenceManager Factory.
     * @return The PersistenceManagerFactory
     */
    public PersistenceManagerFactory getPersistenceManagerFactory()
    {
        // To be implemented by subclasses
        throw new JDOException(LOCALISER_JDO.msg("011003"));
    }

    /**
     * Accessor for the PersistenceManager Factory.
     * @return The PersistenceManagerFactory
     */
    public AbstractPersistenceManagerFactory getAbstractPersistenceManagerFactory()
    {
        return apmf;
    }

    /**
     * Accessor for whether to detach all objects on commit of the transaction.
     * @return Whether to detach all on commit.
     */
    public boolean getDetachAllOnCommit()
    {
        return objectMgr.getDetachAllOnCommit();
    }

    /**
     * Accessor for whether to copy objects on attaching.
     * @return Whether to copy objects on attaching.
     */
    public boolean getCopyOnAttach()
    {
        return objectMgr.getCopyOnAttach();
    }

    /**
     * Acessor for the current FetchPlan
     * @return The FetchPlan
     */
    public FetchPlan getFetchPlan()
    {
        return fetchPlan;
    }

    /**
     * Accessor for whether to ignore the cache.
     * @return Whether to ignore the cache.
     */
    public boolean getIgnoreCache()
    {
        return objectMgr.getIgnoreCache();
    }

    /**
     * Accessor for whether the Persistence Manager is multithreaded.
     * @return Whether to run multithreaded.
     */
    public boolean getMultithreaded()
    {
        return objectMgr.getMultithreaded();
    }

    /**
     * Mutator for whether to detach all objects on commit of the transaction.
     * @param flag Whether to detach all on commit.
     */
    public void setDetachAllOnCommit(boolean flag)
    {
        objectMgr.setDetachAllOnCommit(flag);
    }

    /**
     * Mutator for whether to copy objects on attach.
     * @param flag Whether to copy on attaching
     */
    public void setCopyOnAttach(boolean flag)
    {
        objectMgr.setCopyOnAttach(flag);
    }

    /**
     * Mutator for whether to ignore the cache.
     * @param flag Whether to ignore the cache.
     */
    public void setIgnoreCache(boolean flag)
    {
        objectMgr.setIgnoreCache(flag);
    }

    /**
     * Mutator for whether the Persistence Manager is multithreaded.
     * @param flag Whether to run multithreaded.
     */
    public void setMultithreaded(boolean flag)
    {
        objectMgr.setMultithreaded(flag);
    }

    /**
     * Accessor for the date on the datastore.
     * @return Date on the datastore
     */
    public Date getServerDate()
    {
        try
        {
            return objectMgr.getStoreManager().getDatastoreDate();
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to close the Persistence Manager.
     */
    public synchronized void close()
    {
        userObject = null;
        userObjectMap = null;

        try
        {
            // Close the ObjectManager
            objectMgr.close();
            apmf.releasePersistenceManager(this);
            objectMgr.postClose();
        }
        catch (TransactionActiveOnCloseException tae)
        {
            throw new JDOUserException(tae.getMessage(), this);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Accessor for whether this ObjectManager is closed.
     * @return Whether this manager is closed.
     */
    public boolean isClosed()
    {
        return objectMgr.isClosed();
    }

    /**
     * Accessor for the current transaction.
     * @return The transaction
     */
    public javax.jdo.Transaction currentTransaction()
    {
        assertIsOpen();
        return jdotx;
    }

    /**
     * Method to allow setting of the transaction by a superclass.
     * @param tx The transaction
     */
    protected void setTransaction(org.jpox.Transaction tx)
    {
        jdotx = new JDOTransaction(this,tx);
    }

    // ----------------------------- Eviction --------------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the evict process.
     * @param obj The object to evict
     * @throws JDOUserException thrown if some instances could not be evicted
     */
    private void jdoEvict(Object obj)
    {
        try
        {
            objectMgr.evictObject(obj);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to evict an object from L1 cache.
     * @param obj The object
     * @throws JDOUserException thrown if some instances could not be evicted
     */
    public synchronized void evict(Object obj)
    {
        assertIsOpen();
        jdoEvict(obj);
    }

    /**
     * Method to evict all objects of the specified type (and optionaly its subclasses).
     * @param cls Type of persistable object
     * @param subclasses Whether to include subclasses
     * @deprecated
     */
    public synchronized void evictAll(Class cls, boolean subclasses)
    {
        try
        {
            objectMgr.evictObjects(cls, subclasses);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to evict all objects of the specified type (and optionaly its subclasses).
     * @param cls Type of persistable object
     * @param subclasses Whether to include subclasses
     */
    public synchronized void evictAll(boolean subclasses, Class cls)
    {
        try
        {
            objectMgr.evictObjects(cls, subclasses);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to evict an array of objects from L1 cache.
     * @param pcs The objects
     */
    public synchronized void evictAll(Object[] pcs)
    {
        evictAll(Arrays.asList(pcs));
    }

    /**
     * Method to evict a collection of objects from L1 cache.
     * @param pcs The objects
     * @throws JDOUserException thrown if some instances could not be evicted
     */
    public synchronized void evictAll(Collection pcs)
    {
        assertIsOpen();
        ArrayList failures = new ArrayList();
        Iterator i = pcs.iterator();
        while (i.hasNext())
        {
            try
            {
                jdoEvict(i.next());
            }
            catch (JDOException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010036"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    /**
     * Method to evict all current objects from L1 cache.
     */
    public synchronized void evictAll()
    {
        assertIsOpen();
        objectMgr.evictAllObjects();
    }

    // --------------------------------- Refresh ----------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the refresh process.
     * @param obj The object to refresh
     * @throws JDOUserException thrown if the object could not be refreshed
     */
    private void jdoRefresh(Object obj)
    {
        try
        {
            objectMgr.refreshObject(obj);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to do a refresh of an object.
     * @param obj The Object
     * @throws JDOUserException thrown if the object could not be refreshed
     */
    public synchronized void refresh(Object obj)
    {
        assertIsOpen();
        jdoRefresh(obj);
    }

    /**
     * Method to do a refresh of an array of objects.
     * @param pcs The Objects
     */
    public synchronized void refreshAll(Object[] pcs)
    {
        refreshAll(Arrays.asList(pcs));
    }

    /**
     * Method to do a refresh of a collection of objects.
     * @param pcs The Objects
     * @throws JDOUserException thrown if instances could not be refreshed.
     */
    public synchronized void refreshAll(Collection pcs)
    {
        assertIsOpen();
        ArrayList failures = new ArrayList();
        Iterator iter = pcs.iterator();
        while (iter.hasNext())
        {
            try
            {
                jdoRefresh(iter.next());
            }
            catch (JDOException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010037"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    /**
     * Method to do a refresh of all objects.
     * @throws JDOUserException thrown if instances could not be refreshed.
     */
    public synchronized void refreshAll()
    {
        assertIsOpen();
        try
        {
            objectMgr.refreshAllObjects();
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to do a refresh of objects that failed verification in the
     * exception.
     * @param exc The JDO exception containing the objects that failed
     * @since 1.1
     */
    public synchronized void refreshAll(JDOException exc)
    {
        Object obj = exc.getFailedObject();
        if (obj != null)
        {
            refresh(obj);
        }

        Throwable[] nested_excs = exc.getNestedExceptions();
        if (nested_excs != null)
        {
            for (int i = 0; i < nested_excs.length; i++)
            {
                if (nested_excs[i] instanceof JDOException)
                {
                    refreshAll((JDOException) nested_excs[i]);
                }
            }
        }
    }

    // ------------------------------- Retrieve ------------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the retrieve process.
     * @param obj The object to retrieve
     * @param fgOnly Just retrieve the current fetch group
     * @throws JDOUserException thrown if the object could not be retrieved
     */
    private void jdoRetrieve(Object obj, boolean fgOnly)
    {
        try
        {
            objectMgr.retrieveObject(obj, fgOnly);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to retrieve the fields of an object.
     * @param pc The object
     * @param fgOnly Whether to retrieve the current fetch group fields only
     */
    public synchronized void retrieve(Object pc, boolean fgOnly)
    {
        assertIsOpen();
        jdoRetrieve(pc, fgOnly);
    }

    /**
     * Method to retrieve the fields of an object.
     * @param pc The object
     */
    public synchronized void retrieve(Object pc)
    {
        retrieve(pc, false);
    }

    /**
     * Method to retrieve an array of objects.
     * @param pcs The objects
     */
    public synchronized void retrieveAll(Object[] pcs)
    {
        retrieveAll(Arrays.asList(pcs), false);
    }

    /**
     * Retrieve field values of instances from the store. This tells the
     * <code>PersistenceManager</code> that the application intends to use the
     * instances, and their field values should be retrieved. The fields in the
     * current fetch group must be retrieved, and the implementation might
     * retrieve more fields than the current fetch group.
     * <P>
     * The <code>PersistenceManager</code> might use policy information about
     * the class to retrieve associated instances.
     * @param pcs the instances
     * @param fgOnly whether to retrieve only the current fetch group fields
     */
    public void retrieveAll(Object[] pcs, boolean fgOnly)
    {
        retrieveAll(Arrays.asList(pcs), fgOnly);
    }

    /**
     * Retrieve field values of instances from the store.
     * As the equivalent method but arguments reversed for JDK1.5+.
     * @param fgOnly whether to retrieve only the current fetch group fields
     * @param pcs the instances
     * @since 1.2
     */
    public void retrieveAll(boolean fgOnly, Object[] pcs)
    {
        retrieveAll(Arrays.asList(pcs), fgOnly);
    }

    /**
     * Retrieve field values of instances from the store. This tells the
     * <code>PersistenceManager</code> that the application intends to use the
     * instances, and their field values should be retrieved. The fields in the
     * current fetch group must be retrieved, and the implementation might
     * retrieve more fields than the current fetch group.
     * <P>
     * The <code>PersistenceManager</code> might use policy information about
     * the class to retrieve associated instances.
     * @param pcs the instances
     * @param fgOnly whether to retrieve only the current fetch-group fields
     */
    public void retrieveAll(Collection pcs, boolean fgOnly)
    {
        assertIsOpen();
        ArrayList failures = new ArrayList();
        Iterator i = pcs.iterator();
        while (i.hasNext())
        {
            try
            {
                jdoRetrieve(i.next(), fgOnly);
            }
            catch (RuntimeException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010038"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    /**
     * Method to retrieve a collection of objects. Throws a JDOUserException if
     * instances could not be retrieved.
     * @param pcs The objects
     */
    public synchronized void retrieveAll(Collection pcs)
    {
        retrieveAll(pcs.toArray(), false);
    }

    // ---------------------------------- Make Persistent ---------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the makePersistent process.
     * @param obj The object to persist
     * @throws JDOUserException thrown if the object could not be persisted
     */
    private Object jdoMakePersistent(Object obj)
    {
        try
        {
            return objectMgr.persistObject(obj);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * JDO method to persist an object.
     * Will also attach a previously detached object.
     * @param obj The object
     * @return The persisted object
     */
    public synchronized Object makePersistent(Object obj)
    {
        assertIsOpen();
        assertWritable();
        if (obj == null)
        {
            return null;
        }

        // Persist the object
        return jdoMakePersistent(obj);
    }

    /**
     * JDO method to make persistent an array of objects.
     * @param pcs The objects to persist
     */
    public synchronized Object[] makePersistentAll(Object[] pcs)
    {
        return makePersistentAll(Arrays.asList(pcs)).toArray();
    }

    /**
     * JDO method to make persistent a collection of objects.
     * Throws a JDOUserException if objects could not be made persistent.
     * @param pcs The objects to persist
     */
    public synchronized Collection makePersistentAll(Collection pcs)
    {
        assertIsOpen();
        assertWritable();

        List failures = null;
        Iterator i = pcs.iterator();
        Collection persistedPcs = new ArrayList();
        while (i.hasNext())
        {
            try
            {
                Object persistedPc = jdoMakePersistent(i.next());
                persistedPcs.add(persistedPc);
            }
            catch (RuntimeException e)
            {
                if( failures == null )
                {
                    failures = new ArrayList();
                }
                failures.add(e);
            }
        }
        if (failures != null)
        {
            throw new JDOUserException(LOCALISER.msg("010039"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }

        return persistedPcs;
    }

    // ------------------------------- Delete Persistent ------------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the deletePersistent process.
     * @param obj The object to delete
     * @throws JDOUserException thrown if the object could not be deleted
     */
    private void jdoDeletePersistent(Object obj)
    {
        try
        {
            objectMgr.deleteObject(obj);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * JDO method to delete an object from the datastore.
     * @param obj The object
     */
    public synchronized void deletePersistent(Object obj)
    {
        assertIsOpen();
        assertWritable();

        jdoDeletePersistent(obj);
    }

    /**
     * JDO method to delete an array of objects from the datastore.
     * @param pcs The objects
     */
    public synchronized void deletePersistentAll(Object[] pcs)
    {
        deletePersistentAll(Arrays.asList(pcs));
    }

    /**
     * JDO method to delete a collection of objects from the datastore.
     * Throws a JDOUserException if objects could not be deleted.
     * @param pcs The objects
     */
    public synchronized void deletePersistentAll(Collection pcs)
    {
        assertIsOpen();
        assertWritable();

        ArrayList failures = new ArrayList();
        Iterator i = pcs.iterator();
        while (i.hasNext())
        {
            try
            {
                jdoDeletePersistent(i.next());
            }
            catch (RuntimeException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010040"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    // -------------------------------- Make Transient -----------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the makeTransient process.
     * @param pc The object to make transient
     * @param state FetchPlanState
     * @throws JDOUserException thrown if the object could not be made transient
     */
    private void jdoMakeTransient(Object pc, FetchPlanState state)
    {
        try
        {
            objectMgr.makeObjectTransient(pc, state);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to make transient an object allowing fetching using the fetch plan.
     * @param pc The object
     * @param useFetchPlan Whether to make transient all objects in the fetch plan
     */
    public synchronized void makeTransient(Object pc, boolean useFetchPlan)
    {
        assertIsOpen();

        FetchPlanState state = null;
        if (useFetchPlan)
        {
            // Create a state object to carry the processing state info
            state = new FetchPlanState();
        }
        jdoMakeTransient(pc, state);
    }

    /**
     * Method to make transient an object.
     * This doesn't use the fetch plan and just makes the specified object transient.
     * @param pc The object
     */
    public synchronized void makeTransient(Object pc)
    {
        makeTransient(pc, false);
    }

    /**
     * Method to make transient an array of objects.
     * @param pcs The objects
     */
    public synchronized void makeTransientAll(Object[] pcs)
    {
        makeTransientAll(Arrays.asList(pcs));
    }

    /**
     * Method to make transient an array of objects.
     * @param pcs The objects
     * @param includeFetchPlan Whether to make transient all objects in the fetch plan
     */
    public synchronized void makeTransientAll(Object[] pcs, boolean includeFetchPlan)
    {
        makeTransientAll(Arrays.asList(pcs), includeFetchPlan);
    }

    /**
     * Method to make transient an array of objects.
     * @param includeFetchPlan Whether to make transient all objects in the fetch plan
     * @param pcs The objects
     */
    public synchronized void makeTransientAll(boolean includeFetchPlan, Object[] pcs)
    {
        makeTransientAll(Arrays.asList(pcs), includeFetchPlan);
    }

    /**
     * Method to make transient a collection of objects.
     * @param pcs The objects
     * @param useFetchPlan Whether to use the fetch plan when making transient
     * @throws JDOUserException thrown if objects could not be made transient.
     */
    public synchronized void makeTransientAll(Collection pcs, boolean useFetchPlan)
    {
        assertIsOpen();
        ArrayList failures = new ArrayList();
        Iterator i = pcs.iterator();
        FetchPlanState state = null;
        if (useFetchPlan)
        {
            // Create a state object to carry the processing state info
            state = new FetchPlanState();
        }
        while (i.hasNext())
        {
            try
            {
                jdoMakeTransient(i.next(), state);
            }
            catch (RuntimeException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010041"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    /**
     * Method to make transient a collection of objects.
     * @param pcs The objects
     * @throws JDOUserException thrown if objects could not be made transient.
     */
    public synchronized void makeTransientAll(Collection pcs)
    {
        makeTransientAll(pcs, false);
    }

    // ----------------------------------- Make Transactional --------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the makeTransactional process.
     * @param pc The object to make transactional
     * @throws JDOUserException thrown if the object could not be made transactional
     */
    private void jdoMakeTransactional(Object pc)
    {
        try
        {
            objectMgr.makeObjectTransactional(pc);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to make transactional an object.
     * @param pc The object
     */
    public synchronized void makeTransactional(Object pc)
    {
        assertIsOpen();

        jdoMakeTransactional(pc);
    }

    /**
     * Method to make transactional an array of objects.
     * @param pcs The objects
     */
    public synchronized void makeTransactionalAll(Object[] pcs)
    {
        makeTransactionalAll(Arrays.asList(pcs));
    }

    /**
     * Method to make transactional a collection of objects.
     * @param pcs The objects
     * @throws JDOUserException thrown if objects could not be made transactional
     */
    public synchronized void makeTransactionalAll(Collection pcs)
    {
        assertIsOpen();
        assertActiveTransaction();

        ArrayList failures = new ArrayList();
        Iterator i = pcs.iterator();
        while (i.hasNext())
        {
            try
            {
                jdoMakeTransactional(i.next());
            }
            catch (RuntimeException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010042"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    // ------------------------------ Make NonTransactional -------------------------------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the makeNontransactional process.
     * @param obj The object to make nontransactional
     * @throws JDOUserException thrown if the object could not be made nontransactional
     */
    private void jdoMakeNontransactional(Object obj)
    {
        try
        {
            objectMgr.makeObjectNontransactional(obj);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Method to make nontransactional an object.
     * @param pc The object
     */
    public synchronized void makeNontransactional(Object pc)
    {
        assertIsOpen();
        if (pc == null)
        {
            return;
        }

        // for non transactional read, tx might be not active
        // TODO add verification if is non transactional read = true
        if (objectMgr.getTransaction().isActive())
        {
            assertActiveTransaction();
        }
        // if !transactional and !persistent
        if (!((PersistenceCapable) pc).jdoIsTransactional() && !((PersistenceCapable) pc).jdoIsPersistent())
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011004"));
        }
        // if !transactional and persistent, do nothing
        if (!((PersistenceCapable) pc).jdoIsTransactional() && ((PersistenceCapable) pc).jdoIsPersistent())
        {
            return;
        }
        jdoMakeNontransactional(pc);
    }

    /**
     * Method to make nontransactional an array of objects.
     * @param pcs The objects.
     */
    public synchronized void makeNontransactionalAll(Object[] pcs)
    {
        makeNontransactionalAll(Arrays.asList(pcs));
    }

    /**
     * Method to make nontransactional a collection of objects.
     * @param pcs The objects.
     * @throws JDOUserException thrown if objects could not be made nontransactional
     */
    public synchronized void makeNontransactionalAll(Collection pcs)
    {
        assertIsOpen();
        assertActiveTransaction();

        ArrayList failures = new ArrayList();
        Iterator i = pcs.iterator();
        while (i.hasNext())
        {
            try
            {
                jdoMakeNontransactional(i.next());
            }
            catch (RuntimeException e)
            {
                failures.add(e);
            }
        }
        if (!failures.isEmpty())
        {
            throw new JDOUserException(LOCALISER.msg("010043"), (Exception[]) failures.toArray(new Exception[failures.size()]));
        }
    }

    // ------------------------- Attach/Detach instances -----------------------

    /**
     * JDO Convenience method to wrap any JPOX exceptions for the detachCopy process.
     * @param obj The object to detach a copy of
     * @param state DetachState
     * @throws JDOUserException thrown if the object could not be detached
     */
    private Object jdoDetachCopy(Object obj, FetchPlanState state)
    {
        try
        {
            return objectMgr.detachObjectCopy(obj, state);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * JDO method to detach a persistent object.
     * If the object is of class that is not detachable a transient copy will be returned.
     * If the object is not persistent it will be persisted first before detaching a copy.
     * @param pc The object
     * @return The detached object
     */
    public synchronized Object detachCopy(Object pc)
    {
        assertIsOpen();
        if (pc == null)
        {
            return null;
        }

        try
        {
            objectMgr.assertClassPersistable(pc.getClass());
            assertReadable("detachCopy");

            return jdoDetachCopy(pc, new DetachState(objectMgr.getApiAdapter()));
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Detach the specified objects from the <code>PersistenceManager</code>.
     * The objects returned can be manipulated and re-attached with
     * {@link #makePersistentAll(Object[])}.
     * The detached instances will be unmanaged copies of the specified parameters,
     * and are suitable for serialization and manipulation outside of a JDO environment.
     * When detaching instances, only fields in the current {@link FetchPlan} will be
     * traversed. Thus, to detach a graph of objects, relations to other persistent
     * instances must either be in the <code>default-fetch-group</code>, or in the
     * current custom {@link FetchPlan}.
     * @param pcs the instances to detach
     * @return the detached instances
     */
    public synchronized Object[] detachCopyAll(Object[] pcs)
    {
        return detachCopyAll(Arrays.asList(pcs)).toArray();
    }

    /**
     * Detach the specified objects from the <code>PersistenceManager</code>.
     * @param pcs the instances to detach
     * @return the detached instances
     * @see #detachCopyAll(Object[])
     * @since JDO 2.0
     */
    public synchronized Collection detachCopyAll(Collection pcs)
    {
        assertIsOpen();
        assertReadable("detachCopyAll");

        // Detach the objects
        FetchPlanState state = new DetachState(objectMgr.getApiAdapter());
        List detacheds = new ArrayList();
        for (Iterator it = pcs.iterator(); it.hasNext();)
        {
            Object obj = it.next();
            if (obj == null)
            {
                detacheds.add(null);
            }
            else
            {
                detacheds.add(jdoDetachCopy(obj, state));
            }
        }

        return detacheds;
    }

    // --------------------------------- Queries ----------------------------------------

    /**
     * Construct an empty query instance.
     * @return The query
     */
    public synchronized Query newQuery()
    {
        return newQuery("javax.jdo.query.JDOQL", null);
    }

    /**
     * Construct a query instance from another query. The parameter might be a
     * serialized/restored Query instance from the same JDO vendor but a
     * different execution environment, or the parameter might be currently
     * bound to a PersistenceManager from the same JDO vendor. Any of the
     * elements Class, Filter, IgnoreCache flag, Import declarations, Variable
     * declarations, Parameter declarations, and Ordering from the parameter
     * Query are copied to the new Query instance, but a candidate Collection or
     * Extent element is discarded.
     * @param obj The object to use in the query
     * @return The query
     */
    public synchronized Query newQuery(Object obj)
    {
        if (obj != null && obj instanceof JDOQuery)
        {
            String language = ((JDOQuery)obj).getLanguage();
            return newQuery(language, obj);
        }
        else
        {
            // TODO What situation is this ?
            return newQuery(null, obj);
        }
    }

    /**
     * Construct a query instance using the specified Single-String query.
     * @param query The single-string query
     * @return The Query
     */
    public synchronized Query newQuery(String query)
    {
        return newQuery("javax.jdo.query.JDOQL", query);
    }

    /**
     * Construct a query instance using the specified language and the specified
     * query. The query instance will be of a class defined by the query language.
     * @param language The language parameter for the JDO Query language. This is by default
     * "javax.jdo.query.JDOQL", but in JDO 2.0 can also be "javax.jdo.query.SQL", or vendor provided languages.
     * @param query The query object
     * @return The query
     */
    public synchronized Query newQuery(String language, Object query)
    {
        assertIsOpen();

        if (language == null)
        {
            language = "javax.jdo.query.JDOQL";
        }

        // Check that our store supports the language
        if (!objectMgr.getStoreManager().supportsQueryLanguage(language))
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011006", language));
        }

        org.jpox.store.query.Query internalQuery = null;
        try
        {
            if (query != null && query instanceof JDOQuery)
            {
                // Extract the internal query for generating the next query
                internalQuery =
                    getObjectManager().getOMFContext().getQueryManager().newQuery(language, objectMgr, ((JDOQuery)query).getInternalQuery());
            }
            else
            {
                internalQuery = getObjectManager().getOMFContext().getQueryManager().newQuery(language, objectMgr, query);
            }
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
        return new JDOQuery(this, internalQuery, language);
    }

    /**
     * Construct a query instance with the candidate class specified.
     * @param cls The class to query
     * @return The query
     */
    public synchronized Query newQuery(Class cls)
    {
        Query query = newQuery();
        query.setClass(cls);
        return query;
    }

    /**
     * Construct a query instance with the candidate Extent specified; the
     * candidate class is taken from the Extent.
     * @param cln The extent to query
     * @return The query
     */
    public synchronized Query newQuery(Extent cln)
    {
        Query query = newQuery();
        query.setClass(cln.getCandidateClass());
        query.setCandidates(cln);
        return query;
    }

    /**
     * Construct a query instance with the candidate class and candidate
     * Collection specified.
     * @param cls The class to query
     * @param cln The collection
     * @return The query
     */
    public synchronized Query newQuery(Class cls, Collection cln)
    {
        Query query = newQuery();
        query.setClass(cls);
        query.setCandidates(cln);
        return query;
    }

    /**
     * Construct a query instance with the candidate class and filter specified.
     * @param cls The class to query
     * @param filter A filter to apply
     * @return The query
     */
    public synchronized Query newQuery(Class cls, String filter)
    {
        Query query = newQuery();
        query.setClass(cls);
        query.setFilter(filter);
        return query;
    }

    /**
     * Construct a query instance with the candidate class, the candidate
     * Collection, and filter specified.
     * @param cls The class to query
     * @param cln A collection
     * @param filter A filter to apply
     * @return The query
     */
    public synchronized Query newQuery(Class cls, Collection cln, String filter)
    {
        Query query = newQuery();
        query.setClass(cls);
        query.setCandidates(cln);
        query.setFilter(filter);
        return query;
    }

    /**
     * Construct a query instance with the candidate Extent and filter
     * specified. The candidate class is taken from the Extent.
     * @param cln The extent to query
     * @param filter A filter to apply
     * @return The query
     */
    public synchronized Query newQuery(Extent cln, String filter)
    {
        Query query = newQuery();
        query.setClass(cln.getCandidateClass());
        query.setCandidates(cln);
        query.setFilter(filter);
        return query;
    }

    /**
     * Construct a query instance with the candidate class and the query name.
     * @param cls The class to query
     * @param queryName Name of the query.
     * @return The query
     */
    public synchronized Query newNamedQuery(Class cls, String queryName)
    {
        assertIsOpen();

        // Throw exception on incomplete input
        if (queryName == null)
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011005", queryName, cls));
        }

        // Find the Query for the specified class
        ClassLoaderResolver clr = objectMgr.getClassLoaderResolver();
        QueryMetaData qmd = objectMgr.getMetaDataManager().getMetaDataForQuery(cls, clr, queryName);
        if (qmd == null)
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011005", queryName, cls));
        }

        // Create the Query
        Query query = newQuery(qmd.getLanguage().toString(), qmd.getQuery());
        if (cls != null)
        {
            query.setClass(cls);
            if (!objectMgr.getStoreManager().managesClass(cls.getName()))
            {
                // Load the candidate class since not yet managed
                objectMgr.getStoreManager().addClass(cls.getName(), clr);
            }
        }

        // Optional args that should only be used with SQL
        if (qmd.getLanguage() == QueryLanguage.JDOQL && (qmd.isUnique() || qmd.getResultClass() != null))
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011007", queryName));
        }
        if (qmd.isUnique())
        {
            query.setUnique(true);
        }
        if (qmd.getResultClass() != null)
        {
            // Set the result class, allowing for it being in the same package as the candidate
            Class resultCls = null;
            try
            {
                resultCls = clr.classForName(qmd.getResultClass());
            }
            catch (ClassNotResolvedException cnre)
            {
                try
                {
                    String resultClassName = cls.getPackage().getName() + "." + qmd.getResultClass();
                    resultCls = clr.classForName(resultClassName);
                }
                catch (ClassNotResolvedException cnre2)
                {
                    throw new JDOUserException(LOCALISER_JDO.msg("011008", queryName, qmd.getResultClass()));
                }
            }
            query.setResultClass(resultCls);
        }

        // Add any JPOX extensions
        if (qmd.getLanguage() == QueryLanguage.JPOXSQL)
        {
            // Apply any imports specified
            if (qmd.hasExtension("imports"))
            {
                query.declareImports(qmd.getValueForExtension("imports"));
            }
            // Apply any parameters specified
            if (qmd.hasExtension("parameters"))
            {
                query.declareParameters(qmd.getValueForExtension("parameters"));
            }
        }
        if (qmd.isUnmodifiable())
        {
            query.setUnmodifiable();
        }
        if (qmd.getFetchPlanName() != null)
        {
            // Apply any named FetchPlan to the query
            FetchPlanMetaData fpmd =
                getObjectManager().getMetaDataManager().getMetaDataForFetchPlan(qmd.getFetchPlanName());
            if (fpmd != null)
            {
                org.jpox.FetchPlan fp = new org.jpox.FetchPlan(apmf, clr);
                fp.removeGroup(org.jpox.FetchPlan.DEFAULT);
                FetchGroupMetaData[] fgmds = fpmd.getFetchGroupMetaData();
                for (int i=0;i<fgmds.length;i++)
                {
                    fp.addGroup(fgmds[i].getName());
                }
                fp.setMaxFetchDepth(fpmd.getMaxFetchDepth());
                fp.setFetchSize(fpmd.getFetchSize());
                JDOQuery jdoquery = (JDOQuery)query;
                jdoquery.getInternalQuery().setFetchPlan(fp);
            }
        }

        return query;
    }

    // ------------------------------- Extents ------------------------------------------

    /**
     * Extents are collections of datastore objects managed by the datastore,
     * not by explicit user operations on collections. Extent capability is a
     * boolean property of classes that are persistence capable. If an instance
     * of a class that has a managed extent is made persistent via reachability,
     * the instance is put into the extent implicitly.
     * @param pcClass The class to query
     * @param subclasses Whether to include subclasses in the query.
     * @return returns an Extent that contains all of the instances in the
     * parameter class, and if the subclasses flag is true, all of the instances
     * of the parameter class and its subclasses.
     */
    public synchronized Extent getExtent(Class pcClass, boolean subclasses)
    {
        return new JDOExtent(this,objectMgr.getExtent(pcClass, subclasses));
    }
   
    /**
     * Extents are collections of datastore objects managed by the datastore,
     * not by explicit user operations on collections. Extent capability is a
     * boolean property of classes that are persistence capable. If an instance
     * of a class that has a managed extent is made persistent via reachability,
     * the instance is put into the extent implicitly.
     * @param pcClass The class to query
     * @return returns an Extent that contains all of the instances in the
     * parameter class, and all of the instances of the parameter class and its
     * subclasses.
     * @since 1.1
     */
    public synchronized Extent getExtent(Class pcClass)
    {
        return getExtent(pcClass, true);
    }

    // ----------------------------- New Instances ----------------------------------

    /**
     * Method to generate an instance of an interface, abstract class, or concrete PC class.
     * @param persistenceCapable The class of the interface or abstract class, or concrete class defined in MetaData
     * @return The instance of this type
     */
    public Object newInstance(Class persistenceCapable)
    {
        try
        {
            return objectMgr.newInstance(persistenceCapable);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * This method returns an object id instance corresponding to the pcClass and key arguments.
     * It has 2 modes of operation. Where SingleFieldIdentity is being used the key is the
     * value of the key field. For all other cases the key is the String form of the
     * object id instance.
     * @param pcClass Class of the PersistenceCapable to create the OID for.
     * @param key Value of the key for SingleFieldIdentity, or toString() for other cases
     * @return The new object-id instance
     */
    public Object newObjectIdInstance(Class pcClass, Object key)
    {
        try
        {
            return objectMgr.newObjectId(pcClass, key);
        }
        catch (JPOXException jpe)
        {
            return JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    // ----------------------------- Object Retrieval by Id ----------------------------------

    /**
     * Accessor for the objects currently managed by this PM in the current transaction.
     * @return The managed objects
     */
    public Set getManagedObjects()
    {
        return objectMgr.getManagedObjects();
    }

    /**
     * Accessor for the objects currently managed by this PM in the current transaction.
     * @param classes Classes that we want objects for
     * @return The managed objects
     */
    public Set getManagedObjects(Class[] classes)
    {
        return objectMgr.getManagedObjects(classes);
    }

    /**
     * Accessor for the objects currently managed by this PM in the current transaction.
     * @param states States that we want objects for
     * @return The managed objects
     */
    public Set getManagedObjects(EnumSet states)
    {
        if (states == null)
        {
            return null;
        }

        String[] stateNames = new String[states.size()];
        Iterator iter = states.iterator();
        int i = 0;
        while (iter.hasNext())
        {
            // Convert to strings to avoid using JDO class in Core
            ObjectState state = (ObjectState)iter.next();
            stateNames[i++] = state.toString();
        }
        return objectMgr.getManagedObjects(stateNames);
    }

    /**
     * Accessor for the objects currently managed by this PM in the current transaction.
     * @param states States that we want objects for
     * @param classes Classes that we want objects for
     * @return The managed objects
     */
    public Set getManagedObjects(EnumSet states, Class[] classes)
    {
        if (states == null)
        {
            return null;
        }

        String[] stateNames = new String[states.size()];
        Iterator iter = states.iterator();
        int i = 0;
        while (iter.hasNext())
        {
            // Convert to strings to avoid using JDO class in Core
            ObjectState state = (ObjectState)iter.next();
            stateNames[i++] = state.toString();
        }
        return objectMgr.getManagedObjects(stateNames, classes);
    }

    /**
     * Accessor for an object given the object id.
     * @param id Id of the object.
     * @return The Object
     * @since 1.1
     */
    public synchronized Object getObjectById(Object id)
    {
        return getObjectById(id, true);
    }

    /**
     * Accessor for an object given the object id.
     * @param id Id of the object.
     * @param validate Whether to validate the object state
     * @return The Object
     */
    public synchronized Object getObjectById(Object id, boolean validate)
    {
        assertIsOpen();
        if (id == null)
        {
            throw new JDOUserException(LOCALISER.msg("010044"));
        }

        try
        {
            // Use inheritance checks according to persistence property
            // Really we should be able to assume that SingleFieldIdentity and datastore identity cases are
            // of the class defined by the identity itself since it contains the class name.
            boolean checkInheritance =
                getObjectManager().getOMFContext().getPersistenceConfiguration().getBooleanProperty("org.jpox.findObjectCheckInheritance");
            return objectMgr.findObject(id, validate, checkInheritance, null);
        }
        catch (JPOXException jpe)
        {
            // Convert any JPOX exceptions into what JDO expects
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }

    /**
     * Accessor for the objects given the object ids.
     * @param oids Ids of the objects.
     * @param validate Whether to validate the object state
     * @return The Objects with these ids (in the same order)
     * @since 1.1
     */
    public Collection getObjectsById(Collection oids, boolean validate)
    {
        assertIsOpen();
        if (oids == null || oids.size() == 0)
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011002"));
        }

        Collection objects = new ArrayList(oids.size());
        Iterator iter = oids.iterator();
        while (iter.hasNext())
        {
            Object oid = iter.next();
            // Just use getObjectById to get the object
            objects.add(getObjectById(oid, validate));
        }
        return objects;
    }

    /**
     * Accessor for the objects given the object ids.
     * @param validate Whether to validate the object state
     * @param oids Ids of the objects.
     * @return The Objects with these ids (in the same order)
     * @since 1.2
     */
    public Object[] getObjectsById(boolean validate, Object[] oids)
    {
        return getObjectsById(oids, validate);
    }

    /**
     * Accessor for the objects given the object ids.
     * @param oids Ids of the objects.
     * @param validate Whether to validate the object state
     * @return The Objects with these ids (in the same order)
     * @since 1.1
     */
    public Object[] getObjectsById(Object[] oids, boolean validate)
    {
        assertIsOpen();
        if (oids == null)
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011002"));
        }
        Object[] objects = new Object[oids.length];
        for (int i = 0; i < oids.length; i++)
        {
            // Just use getObjectById to get the object
            objects[i] = getObjectById(oids[i], validate);
        }
        return objects;
    }

    /**
     * Accessor for the objects given the object ids, validating the objects.
     * @param oids Ids of the objects.
     * @return The Objects with these ids (in the same order)
     * @since 1.1
     */
    public Collection getObjectsById(Collection oids)
    {
        return getObjectsById(oids, true);
    }

    /**
     * Accessor for the objects given the object ids, validating the objects.
     * @param oids Ids of the objects.
     * @return The Objects with these ids (in the same order)
     * @since 1.1
     */
    public Object[] getObjectsById(Object[] oids)
    {
        return getObjectsById(oids, true);
    }

    /**
     * Convenience method that exactly matches the behavior of calling
     * pm.getObjectById (pm.newObjectIdInstance (cls, key), true).
     * @param cls Class of the PersistenceCapable
     * @param key Value of the key field for SingleFieldIdentity, or the string value of the key otherwise
     * @return The object for this id.
     * @since 1.1
     */
    public Object getObjectById(Class cls, Object key)
    {
        return getObjectById(newObjectIdInstance (cls, key), true);
    }

    /**
     * Accessor for an object id given the object.
     * @param pc The object
     * @return The Object id
     */
    public Object getObjectId(Object pc)
    {
        assertIsOpen();
        if (pc != null && pc instanceof PersistenceCapable)
        {
            PersistenceCapable p = (PersistenceCapable) pc;
            if (p.jdoIsPersistent() || p.jdoIsDetached())
            {
                return p.jdoGetObjectId();
            }
        }
        return null;
    }

    /**
     * Accessor for the object id of a transactional object given the object.
     * @param pc The object
     * @return The Object id
     */
    public Object getTransactionalObjectId(Object pc)
    {
        assertIsOpen();
        return ((PersistenceCapable) pc).jdoGetTransactionalObjectId();
    }

    /**
     * Accessor for the class of the object id given the class of object.
     * @param cls The class name of the object
     * @return The class name of the object id
     */
    public Class getObjectIdClass(Class cls)
    {
        assertIsOpen();
        if (!getObjectManager().getOMFContext().getApiAdapter().isPersistable(cls) ||
            !hasPersistenceInformationForClass(cls))
        {
            return null;
        }

        ClassLoaderResolver clr = objectMgr.getClassLoaderResolver();
        AbstractClassMetaData cmd = objectMgr.getMetaDataManager().getMetaDataForClass(cls, clr);
        if (cmd.getIdentityType() == IdentityType.DATASTORE)
        {
            return objectMgr.getOMFContext().getDatastoreIdentityClass();
        }
        else if (cmd.getIdentityType() == IdentityType.APPLICATION)
        {
            try
            {
                return this.objectMgr.getClassLoaderResolver().classForName(
                    objectMgr.getMetaDataManager().getMetaDataForClass(cls, clr).getObjectidClass(), null);
            }
            catch (ClassNotResolvedException e)
            {
                String msg = LOCALISER_JDO.msg("011009", cls.getName());
                JPOXLogger.JDO.error(msg);
                throw new JDOException(msg);
            }
        }
        else
        {
            if (cmd.isRequiresExtent())
            {
                return objectMgr.getOMFContext().getDatastoreIdentityClass();
            }
            else
            {
                return SCOID.class;
            }
        }
    }

    // ------------------------------------ User Objects -----------------------------------

    /**
     * Method to put a user object into the PersistenceManager. This is so that
     * multiple users can each have a user object for example. <I>The parameter
     * is not inspected or used in any way by the JDO implementation. </I>
     * @param key The key to store the user object under
     * @param value The object to store
     * @return The previous value for this key
     * @since 1.1
     */
    public synchronized Object putUserObject(Object key, Object value)
    {
        assertIsOpen();
        if (key == null)
        {
            return null;
        }
        if ( userObjectMap == null)
        {
            userObjectMap = new HashMap();
        }
        if (value == null)
        {
            // Remove the object
            return userObjectMap.remove(key);
        }
        else
        {
            // Put the object
            return userObjectMap.put(key, value);
        }
    }

    /**
     * Method to get a user object from the PersistenceManager. This is for user
     * objects which are stored under a key. <I>The parameter is not inspected
     * or used in any way by the JDO implementation. </I>
     * @param key The key to store the user object under
     * @return The user object for that key
     * @since 1.1
     */
    public synchronized Object getUserObject(Object key)
    {
        assertIsOpen();
        if (key == null)
        {
            return null;
        }
        if (userObjectMap==null)
        {
            return null;
        }
        return userObjectMap.get(key);
    }

    /**
     * Method to remove a user object from the PersistenceManager. This is for
     * user objects which are stored under a key. <I>The parameter is not
     * inspected or used in any way by the JDO implementation. </I>
     * @param key The key whose uder object is to be removed.
     * @return The user object that was removed
     * @since 1.1
     */
    public synchronized Object removeUserObject(Object key)
    {
        assertIsOpen();
        if (key == null)
        {
            return null;
        }
        if (userObjectMap==null)
        {
            return null;
        }
        return userObjectMap.remove(key);
    }

    /**
     * The application might manage PersistenceManager instances by using an
     * associated object for bookkeeping purposes. These methods allow the user
     * to manage the associated object. <I>The parameter is not inspected or
     * used in any way by the JDO implementation. </I>
     * @param userObject The object
     */
    public synchronized void setUserObject(Object userObject)
    {
        assertIsOpen();
        this.userObject = userObject;
    }

    /**
     * The application might manage PersistenceManager instances by using an
     * associated object for bookkeeping purposes. These methods allow the user
     * to manage the associated object. <I>The parameter is not inspected or
     * used in any way by the JDO implementation. </I>
     * @return The user object
     */
    public synchronized Object getUserObject()
    {
        assertIsOpen();
        return userObject;
    }

    /**
     * This method flushes all dirty, new, and deleted instances to the
     * datastore. It has no effect if a transaction is not active. If a
     * datastore transaction is active, this method synchronizes the cache with
     * the datastore and reports any exceptions. If an optimistic transaction is
     * active, this method obtains a datastore connection and synchronizes the
     * cache with the datastore using this connection. The connection obtained
     * by this method is held until the end of the transaction.
     */
    public synchronized void flush()
    {
        try
        {
            // Flush all changes to the datastore without letting the store manager hold back
            objectMgr.flush();
        }
        catch (JPOXException jpe)
        {
            if (jpe instanceof JPOXOptimisticException)
            {
                // Optimistic exceptions so convert all nested into JDOOptimisticVerificationException and return as single
                Throwable[] nested = jpe.getNestedExceptions();
                JDOOptimisticVerificationException[] jdoNested = new JDOOptimisticVerificationException[nested.length];
                for (int i=0;i<nested.length;i++)
                {
                    jdoNested[i] =
                        (JDOOptimisticVerificationException)JPOXJDOHelper.getJDOExceptionForJPOXException((JPOXException)nested[i]);
                }
                throw new JDOOptimisticVerificationException(jpe.getMessage(), jdoNested);
            }
            else
            {
                // Convert any JPOX exceptions into what JDO expects
                throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
            }
        }
    }

    /**
     * This method validates the cache with the datastore. It has no effect if a transaction
     * is not active. If a datastore transaction is active, this method verifies the consistency
     * of instances in the cache against the datastore. An implementation might flush instances
     * as if flush() were called, but it is not required to do so.
     * If an optimistic transaction is active, this method obtains a datastore connection
     * and verifies the consistency of the instances in the cache against the datastore. If any
     * inconsistencies are detected, a JDOOptimisticVerificationException is thrown. This
     * exception contains a nested JDOOptimisticVerificationException for each object
     * that failed the consistency check. No datastore resources acquired during the execution
     * of this method are held beyond the scope of this method.
     */
    public void checkConsistency()
    {
        assertIsOpen();

        // If transaction is not active do nothing
        if (!objectMgr.getTransaction().isActive())
        {
            return;
        }

        if (objectMgr.getTransaction().getOptimistic())
        {
            // TODO Implement checkConsistency() for optimistic transactions
            throw new JDOUserException("checkConsistency() not yet implemented for optimistic transactions");
        }
        else
        {
            flush();
        }
    }

    // ------------------------------------- Sequence Management --------------------------------------

    /**
     * Method to retrieve a sequence by name. As per JDO2 spec section 12.14.
     * If the named sequence is not known, throws a JDOUserException.
     * @param sequenceName Fully qualified name of the sequence
     * @return The sequence
     */
    public Sequence getSequence(String sequenceName)
    {
        assertIsOpen();

        SequenceMetaData seqmd = objectMgr.getMetaDataManager().getMetaDataForSequence(objectMgr.getClassLoaderResolver(),sequenceName);
        if (seqmd == null)
        {
            throw new JDOUserException(LOCALISER.msg("017000", sequenceName));
        }

        Sequence seq = null;
        if (seqmd.getFactoryClass() != null)
        {
            // User has specified a factory class
            seq = getAbstractPersistenceManagerFactory().getSequenceForFactoryClass(seqmd.getFactoryClass());
            if (seq == null)
            {
                // Create a new instance of the factory class and obtain the Sequence
                Class factory = objectMgr.getClassLoaderResolver().classForName(seqmd.getFactoryClass());
                if (factory == null)
                {
                    throw new JDOUserException(LOCALISER.msg("017001", sequenceName, seqmd.getFactoryClass()));
                }

                Class[] argTypes = null;
                Object[] arguments = null;
                if (seqmd.getStrategy() != null)
                {
                    argTypes = new Class[2];
                    argTypes[0] = String.class;
                    argTypes[1] = String.class;
                    arguments = new Object[2];
                    arguments[0] = seqmd.getName();
                    arguments[1] = seqmd.getStrategy().toString();
                }
                else
                {
                    argTypes = new Class[1];
                    argTypes[0] = String.class;
                    arguments = new Object[1];
                    arguments[0] = seqmd.getName();
                }

                Method newInstanceMethod;
                try
                {
                    // Obtain the sequence from the static "newInstance(...)" method
                    newInstanceMethod = factory.getMethod("newInstance", argTypes);
                    seq = (Sequence) newInstanceMethod.invoke(null, arguments);
                }
                catch (Exception e)
                {
                    throw new JDOUserException(LOCALISER.msg("017002", seqmd.getFactoryClass(), e.getMessage()));
                }

                // Register the sequence with the PMF
                getAbstractPersistenceManagerFactory().addSequenceForFactoryClass(seqmd.getFactoryClass(), seq);
            }
        }
        else
        {
            seq = (Sequence)objectMgr.getStoreManager().getJPOXSequence(objectMgr, seqmd);
        }
        return seq;
    }

    // ------------------------------------- Lifecycle Listeners --------------------------------------

    /**
     * Method to register a lifecycle listener as per JDO 2.0 spec 12.15.
     * @param listener The instance lifecycle listener to sends events to
     * @param classes The classes that it is interested in
     * @since 1.1
     */
    public void addInstanceLifecycleListener(InstanceLifecycleListener listener, Class[] classes)
    {
        objectMgr.addListener(listener, classes);
    }

    /**
     * Method to remove a currently registered lifecycle listener, as per JDO 2.0 spec 12.15.
     * @param listener The instance lifecycle listener to remove.
     * @since 1.1
     */
    public void removeInstanceLifecycleListener(InstanceLifecycleListener listener)
    {
        objectMgr.removeListener(listener);
    }

    // -------------------------------------- Utility methods ----------------------------------------

    /**
     * Method to assert if this Persistence Manager is open.
     * @throws JDOFatalUserException if the PM is closed.
     */
    protected void assertIsOpen()
    {
        if (isClosed())
        {
            throw new JDOFatalUserException(LOCALISER_JDO.msg("011000"));
        }
    }

    /**
     * Method to assert if the current transaction is active.
     * Throws a TransactionNotActiveException if not active.
     */
    protected void assertActiveTransaction()
    {
        if (!objectMgr.getTransaction().isActive())
        {
            throw new TransactionNotActiveException();
        }
    }

    /**
     * Method to assert if the current transaction is active or non transactional writes are allowed.
     * @throws a TransactionNotWritableException if not active and non transactional writes are disabled
     */
    protected void assertWritable()
    {
        if (!objectMgr.getTransaction().isActive() && !objectMgr.getTransaction().getNontransactionalWrite())
        {
            throw new TransactionNotWritableException();
        }
    }

    /**
     * Method to assert if no active transaction and nontransactionalRead is not set.
     * @throws JDOUserException if the tx is not active and no non-transactional read is available
     */
    protected void assertReadable(String operation)
    {
        if (!objectMgr.getTransaction().isActive() && !objectMgr.getTransaction().getNontransactionalRead())
        {
            throw new JDOUserException(LOCALISER_JDO.msg("011001", operation));
        }
    }

    /**
     * Utility method to check if the specified class has reachable metadata/annotations.
     * @param cls The class to check
     * @return Whether the class has reachable metadata/annotations.
     */
    protected boolean hasPersistenceInformationForClass(Class cls)
    {
        return objectMgr.hasPersistenceInformationForClass(cls);
    }

    /**
     * Accessor for a connection on the datastore. See JDO 2.0 spec section 12.16
     * @return The JDO connection to the datastore
     * @see javax.jdo.PersistenceManager#getDataStoreConnection()
     * @since 1.1
     */
    public JDOConnection getDataStoreConnection()
    {
        try
        {
            return (JDOConnection)objectMgr.getStoreManager().getJPOXConnection(objectMgr);
        }
        catch (JPOXException jpe)
        {
            throw JPOXJDOHelper.getJDOExceptionForJPOXException(jpe);
        }
    }
}
TOP

Related Classes of org.jpox.jdo.AbstractPersistenceManager

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.