Package org.datanucleus.jdo

Source Code of org.datanucleus.jdo.JDOPersistenceManagerFactory

/**********************************************************************
Copyright (c) 2007 Andy Jefferson 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:
2008 Craig Russell - AccessController register of JDOStateManagerImpl
   ...
**********************************************************************/
package org.datanucleus.jdo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.jdo.FetchGroup;
import javax.jdo.JDOException;
import javax.jdo.JDOUnsupportedOptionException;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.datastore.DataStoreCache;
import javax.jdo.datastore.Sequence;
import javax.jdo.listener.InstanceLifecycleListener;
import javax.jdo.spi.JDOImplHelper;
import javax.jdo.spi.JDOPermission;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ConnectionFactory;
import org.datanucleus.ObjectManagerFactoryImpl;
import org.datanucleus.ResourceType;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.TransactionIsolationNotSupportedException;
import org.datanucleus.jdo.metadata.ClassMetadataImpl;
import org.datanucleus.jdo.metadata.InterfaceMetadataImpl;
import org.datanucleus.jdo.metadata.JDOMetadataImpl;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.ClassMetaData;
import org.datanucleus.metadata.FileMetaData;
import org.datanucleus.metadata.InterfaceMetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.PackageMetaData;
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.metadata.TransactionType;
import org.datanucleus.properties.CorePropertyValidator;
import org.datanucleus.state.JDOStateManagerImpl;
import org.datanucleus.store.StoreManager;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

/**
* Implementation of a JDO PersistenceManagerFactory, used to obtain PersistenceManager instances.
* The factory is configurable up to a point when it is frozen. Thereafter nothing can be changed.
*/
public class JDOPersistenceManagerFactory extends ObjectManagerFactoryImpl
    implements PersistenceManagerFactory, ObjectFactory, Referenceable
{
    /** Localisation utility for output messages from jdo. */
    protected static final Localiser LOCALISER_JDO = Localiser.getInstance(
        "org.datanucleus.jdo.Localisation", ObjectManagerFactoryImpl.class.getClassLoader());

    /** The cache of PM's in use. */
    private Set<JDOPersistenceManager> pmCache = new HashSet<JDOPersistenceManager>();

    /** Lifecycle Listeners. */
    protected List<LifecycleListenerForClass> lifecycleListeners;

    /** Map of user-defined sequences keyed by the factory class name. */
    private Map<String, Sequence> sequenceByFactoryClass;

    /** Level 2 Cache. */
    private DataStoreCache datastoreCache = null;

    /** JDO Fetch Groups. */
    private Set<JDOFetchGroup> jdoFetchGroups = null;

    /**
     * Thread-specific state information (instances of {@link OperationInfo}) for the PM proxy.
     */
    private transient ThreadLocal pmProxyThreadLocal = new ThreadLocal()
    {
        protected Object initialValue()
        {
            return null;
        }
    };

    /**
     * Return a new PersistenceManagerFactory with options set according to the given Properties.
     * This method exists for JDO1 compliance whereas in JDO2 the method takes a Map.
     * @param overridingProps The Properties to initialize the PersistenceManagerFactory with.
     * @return A PersistenceManagerFactory with options set according to the given Properties.
     * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
     */
    public synchronized static PersistenceManagerFactory getPersistenceManagerFactory(Properties overridingProps)
    {
        // Extract the properties into a Map allowing for a Properties object being used
        Map overridingMap = new HashMap();
        for (Enumeration e = overridingProps.propertyNames() ; e.hasMoreElements() ;)
        {
            // Make sure we handle default properties too (SUN Properties class oddness)
            String param = (String)e.nextElement();
            overridingMap.put(param, overridingProps.getProperty(param));
        }

        // Create the PMF and freeze it (JDO spec $11.7)
        final JDOPersistenceManagerFactory pmf = new JDOPersistenceManagerFactory(overridingMap);
        pmf.freezeConfiguration();

        return pmf;
    }

    /**
     * Return a new PersistenceManagerFactory with options set according to the given Properties.
     * @param overridingProps The Map of properties to initialize the PersistenceManagerFactory with.
     * @return A PersistenceManagerFactory with options set according to the given Properties.
     * @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
     */
    public synchronized static PersistenceManagerFactory getPersistenceManagerFactory(Map overridingProps)
    {
        // Extract the properties into a Map allowing for a Properties object being used
        Map overridingMap = null;
        if (overridingProps instanceof Properties)
        {
            // Make sure we handle default properties too (SUN Properties class oddness)
            overridingMap = new HashMap();
            for (Enumeration e = ((Properties)overridingProps).propertyNames() ; e.hasMoreElements() ;)
            {
                String param = (String)e.nextElement();
                overridingMap.put(param, ((Properties)overridingProps).getProperty(param));
            }
        }
        else
        {
            overridingMap = overridingProps;
        }

        // Create the PMF and freeze it (JDO spec $11.7)
        final JDOPersistenceManagerFactory pmf = new JDOPersistenceManagerFactory(overridingMap);
        pmf.freezeConfiguration();

        return pmf;
    }

    /**
     * Return a new PersistenceManagerFactory with options set according to the given properties and
     * given overrides.
     * @param overrides Map of properties to override the supplied props (if any)
     * @param props Map of properties to initialise the PMF with
     * @return A PersistenceManagerFactory with options set according to the given Properties
     */
    public synchronized static PersistenceManagerFactory getPersistenceManagerFactory(Map overrides, Map props)
    {
        // Extract the props into a Map allowing for a Properties object being used
        Map propsMap = null;
        if (props instanceof Properties)
        {
            // Make sure we handle default properties too (SUN Properties class oddness)
            propsMap = new HashMap();
            for (Enumeration e = ((Properties)props).propertyNames() ; e.hasMoreElements() ;)
            {
                String param = (String)e.nextElement();
                propsMap.put(param, ((Properties)props).getProperty(param));
            }
        }
        else
        {
            propsMap = props;
        }

        // Extract the overrides into a Map allowing for a Properties object being used
        Map overridesMap = null;
        if (overrides instanceof Properties)
        {
            // Make sure we handle default properties too (SUN Properties class oddness)
            overridesMap = new HashMap();
            for (Enumeration e = ((Properties)overrides).propertyNames() ; e.hasMoreElements() ;)
            {
                String param = (String)e.nextElement();
                overridesMap.put(param, ((Properties)overrides).getProperty(param));
            }
        }
        else
        {
            overridesMap = overrides;
        }

        // Set the properties of the PMF, taking propsMap+overridesMap
        Map overallMap = null;
        if (propsMap != null)
        {
            overallMap = new HashMap(propsMap);
        }
        else
        {
            overallMap = new HashMap();
        }
        if (overridesMap != null)
        {
            overallMap.putAll(overridesMap);
        }

        // Create the PMF and freeze it (JDO spec $11.7)
        final JDOPersistenceManagerFactory pmf = new JDOPersistenceManagerFactory(overallMap);
        pmf.freezeConfiguration();

        return pmf;
    }

    /**
     * Constructs a new JDOPersistenceManagerFactory.
     */
    public JDOPersistenceManagerFactory()
    {
        this(null);
    }

    /**
     * Constructs a new JDOPersistenceManagerFactory.
     * @param props Persistent properties
     */
    public JDOPersistenceManagerFactory(Map props)
    {
        super();

        initialiseProperties(props);
    }

    /**
     * Convenience method to set the API and properties that a PMF will use.
     * Process followed here is as follows :-
     * <ul>
     * <li>Apply any properties that affect startup resources (e.g plugin manager)</li>
     * <li>Create the OMFContext, which sets the default properties for DataNucleus as a whole</li>
     * <li>Set the API, and set the default properties for the API as a whole</li>
     * <li>Apply user properties</li>
     * </ul>
     * @param props The properties to use for this PMF
     */
    protected void initialiseProperties(Map props)
    {
        // Apply any PMF properties that affect startup (before creating OMFContext)
        if (props != null)
        {
            // Possible properties to check for
            String[] startupPropNames = new String[]
               {
                    "datanucleus.plugin.pluginRegistryClassName",
                    "datanucleus.plugin.pluginRegistryBundleCheck",
                    "datanucleus.classLoaderResolverName",
                    "datanucleus.persistenceXmlFilename",
                    "datanucleus.primaryClassLoader"
               };

            Map startupProps = null;
            for (int i=0;i<startupPropNames.length;i++)
            {
                if (props.containsKey(startupPropNames[i]))
                {
                    if (startupProps == null)
                    {
                        startupProps = new HashMap();
                    }
                    startupProps.put(startupPropNames[i], props.get(startupPropNames[i]));
                }
            }
            if (startupProps != null)
            {
                try
                {
                    setOptions(startupProps);
                }
                catch (NucleusException jpe)
                {
                    // Only throw JDOException and subclasses
                    throw NucleusJDOHelper.getJDOExceptionForNucleusException(jpe);
                }
            }
        }

        // Initialise the OMFContext
        initialiseOMFContext();

        // Determine the API to be used by the PMF and apply its default props to the PMF
        String api = "JDO"; // Default to JDO unless specified
        if (props != null && props.get("datanucleus.persistenceApiName") != null)
        {
            api = (String)props.get("datanucleus.persistenceApiName");
        }
        omfContext.setApi(api); // Set the API used by the context
        setOptions(omfContext.getApiAdapter().getDefaultFactoryProperties());

        // Register the StateManager class with JDOImplHelper for security
        AccessController.doPrivileged(new PrivilegedAction()
        {
            public Object run()
            {
                JDOImplHelper.registerAuthorizedStateManagerClass(JDOStateManagerImpl.class);
                return null;
            }
        });

        // Generate the properties to apply to the PMF
        Map pmfProps = new HashMap();

        PersistenceUnitMetaData pumd = null;
        if (props != null)
        {
            String persistenceUnitName = (String)props.get("datanucleus.PersistenceUnitName");
            if (persistenceUnitName == null)
            {
                persistenceUnitName = (String)props.get("javax.jdo.option.PersistenceUnitName");
            }
            if (persistenceUnitName != null)
            {
                // PMF for a "persistence-unit", so add property so the persistence mechanism knows this
                setProperty("datanucleus.PersistenceUnitName", persistenceUnitName);

                try
                {
                    // Obtain any props defined for the persistence-unit
                    pumd = omfContext.getMetaDataManager().getMetaDataForPersistenceUnit(persistenceUnitName);
                    if (pumd != null)
                    {
                        // Add the properties for the unit
                        if (pumd.getProperties() != null)
                        {
                            pmfProps.putAll(pumd.getProperties());
                        }
                    }
                    else
                    {
                        throw new JDOUserException(LOCALISER_JDO.msg("012004", persistenceUnitName));
                    }

                    if (omfContext.getApi().equalsIgnoreCase("JPA"))
                    {
                        pumd.clearJarFiles(); // Don't use JARs when in J2SE for JPA
                    }
                }
                catch (NucleusException jpe)
                {
                    throw new JDOUserException(LOCALISER_JDO.msg("012005", persistenceUnitName));
                }
            }
        }

        // Append on any user properties
        if (props != null)
        {
            pmfProps.putAll(props);
            if (!pmfProps.containsKey("datanucleus.TransactionType") &&
                !pmfProps.containsKey("javax.jdo.option.TransactionType"))
            {
                // Default to RESOURCE_LOCAL txns
                pmfProps.put("datanucleus.TransactionType", TransactionType.RESOURCE_LOCAL.toString());
            }
            else
            {
                // let TransactionType.JTA imply ResourceType.JTA
                String transactionType = pmfProps.get("datanucleus.TransactionType")!=null? (String)pmfProps.get("datanucleus.TransactionType"):(String)pmfProps.get("javax.jdo.option.TransactionType");
                if (TransactionType.JTA.toString().equalsIgnoreCase(transactionType))
                {
                    pmfProps.put(ConnectionFactory.DATANUCLEUS_CONNECTION_RESOURCE_TYPE, ResourceType.JTA.toString());
                    pmfProps.put(ConnectionFactory.DATANUCLEUS_CONNECTION2_RESOURCE_TYPE, ResourceType.JTA.toString());
                }
            }
        }
        else
        {
            pmfProps.put("datanucleus.TransactionType", TransactionType.RESOURCE_LOCAL.toString());
        }

        // Apply the properties to the PMF
        try
        {
            setOptions(pmfProps);
        }
        catch (NucleusException jpe)
        {
            // Only throw JDOException and subclasses
            throw NucleusJDOHelper.getJDOExceptionForNucleusException(jpe);
        }

        if (pumd != null)
        {
            // Initialise the MetaDataManager with all files/classes for this persistence-unit
            // This is done now that all PMF properties are set (including the persistence-unit props)
            try
            {
                omfContext.getMetaDataManager().loadPersistenceUnit(pumd, null);
            }
            catch (NucleusException jpe)
            {
                throw new JDOException(jpe.getMessage(),jpe);
            }
        }

        if (props != null)
        {
            // Process any lifecycle listeners defined in persistent properties
            Iterator propsIter = props.keySet().iterator();
            while (propsIter.hasNext())
            {
                String key = (String)propsIter.next();
                if (key.startsWith("javax.jdo.listener.InstanceLifecycleListener"))
                {
                    String listenerClsName = key.substring(45);
                    String listenerClasses = (String)props.get(key);
                    ClassLoaderResolver clr = omfContext.getClassLoaderResolver(null);
                    Class listenerCls = null;
                    try
                    {
                        listenerCls = clr.classForName(listenerClsName);
                    }
                    catch (ClassNotResolvedException cnre)
                    {
                        throw new JDOUserException(LOCALISER_JDO.msg("012022", listenerClsName));
                    }

                    InstanceLifecycleListener listener = null;

                    // Find method getInstance()
                    Method method = ClassUtils.getMethodForClass(listenerCls, "getInstance", null);
                    if (method != null)
                    {
                        // Create instance via getInstance()
                        try
                        {
                            listener = (InstanceLifecycleListener)method.invoke(null);
                        }
                        catch (Exception e)
                        {
                            throw new JDOUserException(LOCALISER_JDO.msg("012021", listenerClsName), e);
                        }
                    }
                    else
                    {
                        // Try default constructor
                        try
                        {
                            listener = (InstanceLifecycleListener)listenerCls.newInstance();
                        }
                        catch (Exception e)
                        {
                            throw new JDOUserException(LOCALISER_JDO.msg("012020", listenerClsName), e);
                        }
                    }

                    Class[] classes = null;
                    if (!StringUtils.isWhitespace(listenerClasses))
                    {
                        String[] classNames = StringUtils.split(listenerClasses, ",");
                        classes = new Class[classNames.length];
                        for (int i=0;i<classNames.length;i++)
                        {
                            classes[i] = clr.classForName(classNames[i]);
                        }
                    }

                    addInstanceLifecycleListener(listener, classes);
                }
            }
        }
    }

    /**
     * Freezes the current configuration.
     * @throws JDOException if the configuration was invalid or inconsistent in some way
     */
    protected void freezeConfiguration()
    {
        if (configurable)
        {
            if (omfContext == null)
            {
                // User has instantiated a PMF via the default constructor and JavaBean setters,
                // so make sure we have the OMFContext etc ready
                initialiseOMFContext();
            }

            try
            {
                // Log the PMF configuration
                logConfiguration();

                // Set user classloader
                ClassLoaderResolver clr = getOMFContext().getClassLoaderResolver(null);
                clr.registerUserClassLoader((ClassLoader)getProperty("datanucleus.primaryClassLoader"));

                // Set up the StoreManager
                initialiseStoreManager(clr);

                // Set up the Level 2 Cache
                initialiseLevel2Cache();

                if (cache != null)
                {
                    datastoreCache = new JDODataStoreCache(cache);
                }

                configurable = false;
            }
            catch (TransactionIsolationNotSupportedException inse)
            {
                throw new JDOUnsupportedOptionException(inse.getMessage());
            }
            catch (NucleusException jpe)
            {
                throw NucleusJDOHelper.getJDOExceptionForNucleusException(jpe);
            }
        }
    }

    /**
     * Get an instance of <tt>PersistenceManager</tt> from this factory. The instance has default values for options.
     * <p>After the first use of getPersistenceManager, no "set" methods will succeed.</p>
     * @return a <tt>PersistenceManager</tt> instance with default options.
     */
    public synchronized PersistenceManager getPersistenceManager()
    {
        // Just relay to other getPersistenceManager() method
        return getPersistenceManager(
            this.getPersistenceConfiguration().getStringProperty("datanucleus.ConnectionUserName"),
            this.getPersistenceConfiguration().getStringProperty("datanucleus.ConnectionPassword"));
    }

    /**
     * Get an instance of <tt>PersistenceManager</tt> from this factory.
     * The instance has default values for options. The parameters userid/password are used when obtaining
     * datastore connections from the connection pool.
     * <p>After the first use of getPersistenceManager, no "set" methods will succeed.</p>
     * @param userName  the user name for the connection
     * @param password  the password for the connection
     * @return <tt>PersistenceManager</tt> instance with default options.
     */
    public synchronized PersistenceManager getPersistenceManager(String userName, String password)
    {
        assertIsOpen();

        // Freeze the PMF config now that we are handing out PM's
        freezeConfiguration();

        JDOPersistenceManager pm = newPM(this, userName, password);

        if (lifecycleListeners != null)
        {
            // Add PMF lifecycle listeners to the PM
            Iterator listenerIter = lifecycleListeners.iterator();
            while (listenerIter.hasNext())
            {
                LifecycleListenerForClass listener = (LifecycleListenerForClass) listenerIter.next();
                pm.addInstanceLifecycleListener(listener.getListener(), listener.getClasses());
            }
        }

        getPmCache().add(pm);

        return pm;
    }

    /**
     * Construct a {@link JDOPersistenceManager}.  Override if you want to construct a subclass instead.
     */
    protected JDOPersistenceManager newPM(JDOPersistenceManagerFactory jdoPmf, String userName, String password)
    {
        return new JDOPersistenceManager(jdoPmf, userName, password);
    }

    /**
     * Equality operator.
     * @param obj Object to compare against
     * @return Whether the objects are the same.
     */
    public synchronized boolean equals(Object obj)
    {
        if (obj == this)
        {
            return true;
        }

        if (!(obj instanceof JDOPersistenceManagerFactory))
        {
            return false;
        }

        return super.equals(obj);
    }

    /**
     * Create a PMF using the (JNDI) location or reference information specified.
     * @param obj The object
     * @param name Name of the object relative to the context
     * @param ctx The context
     * @param env properties used for creating the object
     * @return The PMF instance
     * @throws Exception If an error occurs generating the referenced object
     */
    public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable env)
    throws Exception
    {
        JDOPersistenceManagerFactory pmf = null;
        if (NucleusLogger.NAMING.isDebugEnabled())
        {
            NucleusLogger.NAMING.debug("Creating PersistenceManagerFactory instance via JNDI with values "+
                                    "[object] " + (obj == null ? "" : obj.toString()) + " " +
                                    "[name] " + (name == null ? "" : name.toString()) + " " +
                                    "[context] " + (ctx == null ? "" : ctx.toString()) + " " +
                                    "[env] " + (env == null ? "" : env.toString()) + " ");
        }

        if (obj instanceof Reference)
        {
            Reference ref = (Reference) obj;
            if (ref.getClassName().equals(JDOClassNameConstants.JDOPersistenceManagerFactory) ||
                ref.getClassName().equals(JDOClassNameConstants.JAVAX_JDO_PersistenceManagerFactory))
            {
                // Extract the properties to use for PMF creation
                Properties p = new Properties();
                for (Enumeration e = ref.getAll(); e.hasMoreElements();)
                {
                    StringRefAddr sra = (StringRefAddr) e.nextElement();
                    p.setProperty(sra.getType(), (String) sra.getContent());
                }

                // Create the PMF
                pmf = new JDOPersistenceManagerFactory(p);

                // Freeze the PMF config now that we are handing out PM's : see JDO 1.0.1 [11.7]
                pmf.freezeConfiguration();

                if (NucleusLogger.NAMING.isDebugEnabled())
                {
                    NucleusLogger.NAMING.debug(LOCALISER_JDO.msg("012006", name.toString()));
                }
            }
            else
            {
                NucleusLogger.NAMING.warn(LOCALISER_JDO.msg("012007",
                    ref.getClassName(), JDOClassNameConstants.JDOPersistenceManagerFactory));
            }
        }
        else
        {
            NucleusLogger.NAMING.warn(LOCALISER_JDO.msg("012008", (obj.getClass().getName())));
        }
        return pmf;
    }

    /**
     * Retrieves the (JNDI) reference of this PMF object.
     * @return The reference
     */
    public Reference getReference()
    {
        Reference rc = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try
        {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            rc = new Reference(JDOClassNameConstants.JAVAX_JDO_PersistenceManagerFactory,
                JDOClassNameConstants.JDOPersistenceManagerFactory, null);
           
            Map p = getOptions();
            for (Iterator i = p.keySet().iterator(); i.hasNext();)
            {
                String key = (String) i.next();
                if (p.get(key) instanceof String)
                {
                    String value = (String) p.get(key);
                    rc.add(new StringRefAddr(key, value));
                    if (NucleusLogger.NAMING.isDebugEnabled())
                    {
                        NucleusLogger.NAMING.debug(LOCALISER_JDO.msg("012009", key, value));
                    }
                }
                else if (p.get(key) instanceof Long)
                {
                    String value = "" + p.get(key);
                    rc.add(new StringRefAddr(key, value));
                    if (NucleusLogger.NAMING.isDebugEnabled())
                    {
                        NucleusLogger.NAMING.debug(LOCALISER_JDO.msg("012009", key, value));
                    }
                }
                else if (p.get(key) instanceof Integer)
                {
                    String value = "" + p.get(key);
                    rc.add(new StringRefAddr(key, value));
                    if (NucleusLogger.NAMING.isDebugEnabled())
                    {
                        NucleusLogger.NAMING.debug(LOCALISER_JDO.msg("012009", key, value));
                    }
                }
                else if (p.get(key) instanceof Boolean)
                {
                    String value = (((Boolean)p.get(key)).booleanValue() ? "true" : "false");
                    rc.add(new StringRefAddr(key, value));
                    if (NucleusLogger.NAMING.isDebugEnabled())
                    {
                        NucleusLogger.NAMING.debug(LOCALISER_JDO.msg("012009", key, value));
                    }
                }
                else
                {
                    NucleusLogger.NAMING.warn(LOCALISER_JDO.msg("012010", key));
                }
            }
            if (NucleusLogger.NAMING.isDebugEnabled())
            {
                if (p.isEmpty())
                {
                    NucleusLogger.NAMING.debug(LOCALISER_JDO.msg("012011"));
                }
            }
        }
        catch (IOException ex)
        {
            NucleusLogger.NAMING.error(ex.getMessage());
            throw new NucleusException(ex.getMessage(),ex);
        }
        return rc;
    }

    /**
     * Accessor for the PersistenceManager proxy object
     * @return The PMF proxy
     */
    public PersistenceManager getPersistenceManagerProxy()
    {
        return new JDOPersistenceManagerProxy(this);
    }

    /**
     * Convenience accessor for the thread-local delegate PM for this thread.
     * If no thread-local is set then creates a new PM.
     * @return The real PM to delegate to for this thread
     */
    JDOPersistenceManager getPMProxyDelegate()
    {
        JDOPersistenceManager pm = (JDOPersistenceManager)pmProxyThreadLocal.get();
        if (pm == null)
        {
            // No delegate for this thread so create one and store it
            pm = newPM(this,
                this.getPersistenceConfiguration().getStringProperty("datanucleus.ConnectionUserName"),
                this.getPersistenceConfiguration().getStringProperty("datanucleus.ConnectionPassword"));
            pmProxyThreadLocal.set(pm);
        }
        return pm;
    }

    /**
     * Convenience method to clear the thread-local delegate PM that we refer to.
     * This is invoked when the proxy PM has close() invoked.
     */
    void clearPMProxyDelegate()
    {
        pmProxyThreadLocal.remove();
    }

    /**
     * Return non-configurable properties of this PersistenceManagerFactory.
     * Properties with keys VendorName and VersionNumber are required.  Other keys are optional.
     * @return the non-configurable properties of this PersistenceManagerFactory.
     */
    public Properties getProperties()
    {
        Properties props = new Properties();

        props.setProperty("VendorName", getVendorName());
        props.setProperty("VersionNumber", getVersionNumber());

        return props;
    }

    /**
     * The application can determine from the results of this method which
     * optional features, and which query languages are supported by the JDO
     * implementation. Se esection 11.6 of the JDO 2 specification.
     * @return A Collection of String representing the supported options.
     */
    public Collection<String> supportedOptions()
    {
        // Generate the list of supported options, taking the general options we support and removing
        // any that the particular StoreManager doesn't support
        List options = new ArrayList(Arrays.asList(OPTION_ARRAY));
        StoreManager storeMgr = getOMFContext().getStoreManager();
        if (storeMgr != null)
        {
            Collection storeMgrOptions = storeMgr.getSupportedOptions();
            if (!storeMgrOptions.contains("DatastoreIdentity"))
            {
                options.remove("javax.jdo.option.DatastoreIdentity");
            }
            if (!storeMgrOptions.contains("ApplicationIdentity"))
            {
                options.remove("javax.jdo.option.ApplicationIdentity");
            }
            if (!storeMgrOptions.contains("OptimisticTransaction"))
            {
                options.remove("javax.jdo.option.Optimistic");
            }
            if (!storeMgr.supportsQueryLanguage("JDOQL"))
            {
                options.remove("javax.jdo.query.JDOQL");
            }
            if (!storeMgr.supportsQueryLanguage("SQL"))
            {
                options.remove("javax.jdo.query.SQL");
            }

            if (storeMgrOptions.contains("TransactionIsolationLevel.read-committed"))
            {
                options.add("javax.jdo.option.TransactionIsolationLevel.read-committed");
            }
            if (storeMgrOptions.contains("TransactionIsolationLevel.read-uncommitted"))
            {
                options.add("javax.jdo.option.TransactionIsolationLevel.read-uncommitted");
            }
            if (storeMgrOptions.contains("TransactionIsolationLevel.repeatable-read"))
            {
                options.add("javax.jdo.option.TransactionIsolationLevel.repeatable-read");
            }
            if (storeMgrOptions.contains("TransactionIsolationLevel.serializable"))
            {
                options.add("javax.jdo.option.TransactionIsolationLevel.serializable");
            }
            if (storeMgrOptions.contains("TransactionIsolationLevel.snapshot"))
            {
                options.add("javax.jdo.option.TransactionIsolationLevel.snapshot");
            }
            if (storeMgrOptions.contains("Query.Cancel"))
            {
                options.add("javax.jdo.option.QueryCancel");
            }
        }

        return Collections.unmodifiableList(options);
    }

    /**
     * The JDO spec optional features that DataNucleus supports.
     * See JDO 2.0 spec section 11.6 for the full list of possibilities.
     **/
    private static final String[] OPTION_ARRAY =
    {
        "javax.jdo.option.TransientTransactional",
        "javax.jdo.option.NontransactionalWrite",
        "javax.jdo.option.NontransactionalRead",
        "javax.jdo.option.RetainValues",
        "javax.jdo.option.Optimistic",
        "javax.jdo.option.ApplicationIdentity",
        "javax.jdo.option.DatastoreIdentity",
        "javax.jdo.option.NonDurableIdentity",
        "javax.jdo.option.BinaryCompatibility",
        "javax.jdo.option.GetDataStoreConnection",
        "javax.jdo.option.GetJDBCConnection",
        "javax.jdo.option.version.DateTime",
        "javax.jdo.option.PreDirtyEvent",
        // "javax.jdo.option.ChangeApplicationIdentity",

        // Types
        "javax.jdo.option.ArrayList",
        "javax.jdo.option.LinkedList",
        "javax.jdo.option.TreeSet",
        "javax.jdo.option.TreeMap",
        "javax.jdo.option.Vector",
        "javax.jdo.option.List",
        "javax.jdo.option.Stack", // Not a listed JDO2 feature
        "javax.jdo.option.Map", // Not a listed JDO2 feature
        "javax.jdo.option.HashMap", // Not a listed JDO2 feature
        "javax.jdo.option.Hashtable", // Not a listed JDO2 feature
        "javax.jdo.option.SortedSet", // Not a listed JDO2 feature
        "javax.jdo.option.SortedMap", // Not a listed JDO2 feature
        "javax.jdo.option.Array",
        "javax.jdo.option.NullCollection",

        // ORM
        "javax.jdo.option.mapping.HeterogeneousObjectType",
        "javax.jdo.option.mapping.HeterogeneousInterfaceType",
        "javax.jdo.option.mapping.JoinedTablePerClass",
        "javax.jdo.option.mapping.JoinedTablePerConcreteClass",
        "javax.jdo.option.mapping.NonJoinedTablePerConcreteClass",
        // "javax.jdo.option.mapping.RelationSubclassTable", // Not yet supported for multiple subclasses

        // Query Languages
        "javax.jdo.query.SQL",
        "javax.jdo.query.JDOQL", // Not a listed optional feature
        "javax.jdo.option.UnconstrainedQueryVariables"
    };

    /**
     * Return the Set of PersistenceManagers actually in cache
     * @return Set this contains instances of PersistenceManagers in the cache
     */
    protected Set<JDOPersistenceManager> getPmCache()
    {
        return pmCache;
    }

    /**
     * Remove a PersistenceManager from the cache
     * Only the PersistenceManager is allowed to call this method
     * @param om  the PersistenceManager to be removed from cache
     */
    public void releasePersistenceManager(JDOPersistenceManager om)
    {
        getPmCache().remove(om);
    }

    /**
     * Asserts that the PMF is open.
     * @throws JDOUserException if it is already closed
     */
    protected void assertIsOpen()
    {
        try
        {
            super.assertIsOpen();
        }
        catch (NucleusException jpe)
        {
            // Comply with Section 11.4 of the JDO2 spec (throw JDOUserException if already closed)
            throw NucleusJDOHelper.getJDOExceptionForNucleusException(jpe);
        }
    }

    /**
     * Close this PersistenceManagerFactory. Check for JDOPermission("closePersistenceManagerFactory")
     * and if not authorized, throw SecurityException.
     * <P>If the authorization check succeeds, check to see that all PersistenceManager instances obtained
     * from this PersistenceManagerFactory have no active transactions. If any PersistenceManager instances
     * have an active transaction, throw a JDOUserException, with one nested JDOUserException for each
     * PersistenceManager with an active Transaction.
     * <P>If there are no active transactions, then close all PersistenceManager instances obtained from
     * this PersistenceManagerFactory, mark this PersistenceManagerFactory as closed, disallow
     * getPersistenceManager methods, and allow all other get methods. If a set method or getPersistenceManager
     * method is called after close, then JDOUserException is thrown.
     * @see javax.jdo.PersistenceManagerFactory#close()
     */
    public synchronized void close()
    {
        assertIsOpen();

        SecurityManager secmgr = System.getSecurityManager();
        if (secmgr != null)
        {
            // checkPermission will throw SecurityException if not authorized
            secmgr.checkPermission(JDOPermission.CLOSE_PERSISTENCE_MANAGER_FACTORY);
        }

        // iterate though the list of pms to release resources
        Iterator<JDOPersistenceManager> pms = new HashSet(getPmCache()).iterator();
        Set exceptions = new HashSet();
        while (pms.hasNext())
        {
            try
            {
                pms.next().close();
            }
            catch (JDOUserException ex)
            {
                exceptions.add(ex);
            }
        }
        if (!exceptions.isEmpty())
        {
            throw new JDOUserException(LOCALISER_JDO.msg("012002"),(Throwable[])exceptions.toArray(new Throwable[exceptions.size()]));
        }

        if (jdoFetchGroups != null)
        {
            jdoFetchGroups.clear();
        }

        // Let superclass close its resources
        super.close();
    }

    /**
     * Accessor for the DataStore (level 2) Cache
     * @return The datastore cache
     */
    public DataStoreCache getDataStoreCache()
    {
        freezeConfiguration();

        return datastoreCache;
    }

    /**
     * Set the user name for the data store connection.
     * @param userName the user name for the data store connection.
     */
    public synchronized void setConnectionUserName(String userName)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionUserName", userName);
    }

    /**
     * Set the password for the data store connection.
     * @param password the password for the data store connection.
     */
    public synchronized void setConnectionPassword(String password)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionPassword", password);
    }

    /**
     * Set the URL for the data store connection.
     * @param url the URL for the data store connection.
     */
    public synchronized void setConnectionURL(String url)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionURL", url);
    }

    /**
     * Set the driver name for the data store connection.
     * @param driverName the driver name for the data store connection.
     */
    public synchronized void setConnectionDriverName(String driverName)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionDriverName", driverName);
    }

    /**
     * Set the name for the data store connection factory.
     * @param connectionFactoryName name of the data store connection factory.
     */
    public synchronized void setConnectionFactoryName(String connectionFactoryName)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionFactoryName", connectionFactoryName);
    }

    /**
     * Set the data store connection factory.  JDO implementations
     * will support specific connection factories.  The connection
     * factory interfaces are not part of the JDO specification.
     * @param connectionFactory the data store connection factory.
     */
    public synchronized void setConnectionFactory(Object connectionFactory)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionFactory", connectionFactory);
    }

    /**
     * Set the name for the second data store connection factory.  This is
     * needed for managed environments to get nontransactional connections for
     * optimistic transactions.
     * @param connectionFactoryName name of the data store connection factory.
     */
    public synchronized void setConnectionFactory2Name(String connectionFactoryName)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionFactory2Name", connectionFactoryName);
    }

    /**
     * Set the second data store connection factory.  This is
     * needed for managed environments to get nontransactional connections for
     * optimistic transactions.  JDO implementations
     * will support specific connection factories.  The connection
     * factory interfaces are not part of the JDO specification.
     * @param connectionFactory the data store connection factory.
     */
    public synchronized void setConnectionFactory2(Object connectionFactory)
    {
        assertConfigurable();
        setProperty("datanucleus.ConnectionFactory2", connectionFactory);
    }

    /**
     * Set the default Multithreaded setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @param flag the default Multithreaded setting.
     */
    public synchronized void setMultithreaded(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.Multithreaded", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default Optimistic setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @param flag the default Optimistic setting.
     */
    public synchronized void setOptimistic(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.Optimistic", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default RetainValues setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @param flag the default RetainValues setting.
     */
    public synchronized void setRetainValues(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.RetainValues", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default RestoreValues setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @param flag the default RestoreValues setting.
     */
    public synchronized void setRestoreValues(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.RestoreValues", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default NontransactionalRead setting for all
     * <tt>PersistenceManager</tt> instances obtained from this factory.
     * @param flag the default NontransactionalRead setting.
     */
    public synchronized void setNontransactionalRead(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.NontransactionalRead", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default NontransactionalWrite setting for all
     * <tt>PersistenceManager</tt> instances obtained from this factory.
     * @param flag the default NontransactionalWrite setting.
     */
    public synchronized void setNontransactionalWrite(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.NontransactionalWrite", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default IgnoreCache setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @param flag the default IgnoreCache setting.
     */
    public synchronized void setIgnoreCache(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.IgnoreCache", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Mutator for the DetachAllOnCommit setting.
     * @param flag the default DetachAllOnCommit setting.
     */
    public synchronized void setDetachAllOnCommit(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.DetachAllOnCommit", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Mutator for the CopyOnAttach setting.
     * @param flag the default CopyOnAttach setting.
     */
    public synchronized void setCopyOnAttach(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.CopyOnAttach", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the name for any mapping, used in searching for ORM/Query metadata files.
     * @param mapping the mapping name
     */
    public synchronized void setMapping(String mapping)
    {
        assertConfigurable();
        setProperty("datanucleus.Mapping", mapping);
    }

    /**
     * Mutator for the catalog to use for this persistence factory.
     * @param catalog Name of the catalog
     */
    public synchronized void setCatalog(String catalog)
    {
        assertConfigurable();
        setProperty("datanucleus.mapping.Catalog", catalog);
    }

    /**
     * Mutator for the schema to use for this persistence factory.
     * @param schema Name of the schema
     */
    public synchronized void setSchema(String schema)
    {
        assertConfigurable();
        setProperty("datanucleus.mapping.Schema", schema);
    }

    /**
     * Mutator for the timeout to use for all queries.
     * @param timeout Query timeout interval (millisecs)
     */
    public synchronized void setQueryTimeoutMillis(Integer timeout)
    {
        assertConfigurable();
        setProperty("datanucleus.query.timeout", timeout);
    }

    /**
     * Mutator for the transaction type to use for this persistence factory.
     * @param type Transaction type
     */
    public synchronized void setTransactionType(String type)
    {
        assertConfigurable();
        boolean validated = new CorePropertyValidator().validate("datanucleus.TransactionType", type);
        if (validated)
        {
            setProperty("datanucleus.TransactionType", type);
        }
        else
        {
            throw new JDOUserException(LOCALISER.msg("008012", "javax.jdo.option.TransactionType", type));
        }
    }

    /**
     * Mutator for the name of the persistence unit.
     * @param name Name of the persistence unit
     */
    public synchronized void setPersistenceUnitName(String name)
    {
        assertConfigurable();
        setProperty("datanucleus.PersistenceUnitName", name);
    }

    /**
     * Mutator for the filename of the persistence.xml file.
     * This is for the case where an application has placed the persistence.xml somewhere else maybe
     * outside the CLASSPATH.
     * @param name Filename of the persistence unit
     */
    public synchronized void setPersistenceXmlFilename(String name)
    {
        assertConfigurable();
        setProperty("datanucleus.persistenceXmlFilename", name);
    }

    /**
     * Mutator for the name of the persistence factory.
     * @param name Name of the persistence factory (if any)
     */
    public synchronized void setName(String name)
    {
        assertConfigurable();
        setProperty("datanucleus.Name", name);
    }

    /**
     * Mutator for the timezone id of the datastore server.
     * If not set assumes that it is running in the same timezone as this JVM.
     * @param id Timezone Id to use
     */
    public void setServerTimeZoneID(String id)
    {
        boolean validated = new CorePropertyValidator().validate("datanucleus.ServerTimeZoneID", id);
        if (validated)
        {
            setProperty("datanucleus.ServerTimeZoneID", id);
        }
        else
        {
            throw new JDOUserException("Invalid TimeZone ID specified");
        }
    }

    /**
     * Set the readOnly setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @param flag the default readOnly setting.
     */
    public synchronized void setReadOnly(boolean flag)
    {
        assertConfigurable();
        setProperty("datanucleus.ReadOnly", flag ? Boolean.TRUE : Boolean.FALSE);
    }

    /**
     * Set the default isolation level for transactions.
     * @param level Level
     */
    public void setTransactionIsolationLevel(String level)
    {
        assertConfigurable();

        if (omfContext != null && omfContext.getStoreManager() != null &&
            !omfContext.getStoreManager().getSupportedOptions().contains("TransactionIsolationLevel." + level))
        {
            throw new JDOUnsupportedOptionException("Isolation level \"" + level + "\" is not supported for this datastore");
        }

        // Reset to "read-committed" if passed in as null
        setProperty("datanucleus.transactionIsolation", level != null ? level : "read-committed");
    }

    /**
     * Get the user name for the data store connection.
     * @return the user name for the data store connection.
     */
    public String getConnectionUserName()
    {
        return getStringProperty("datanucleus.ConnectionUserName");
    }

    /**
     * Get the password for the data store connection.
     * @return the password for the data store connection.
     */
    public String getConnectionPassword()
    {
        return getStringProperty("datanucleus.ConnectionPassword");
    }

    /**
     * Get the URL for the data store connection.
     * @return the URL for the data store connection.
     */
    public String getConnectionURL()
    {
        return getStringProperty("datanucleus.ConnectionURL");
    }

    /**
     * Get the driver name for the data store connection.
     * @return the driver name for the data store connection.
     */
    public String getConnectionDriverName()
    {
        return getStringProperty("datanucleus.ConnectionDriverName");
    }

    /**
     * Get the name for the data store connection factory.
     * @return the name of the data store connection factory.
     */
    public String getConnectionFactoryName()
    {
        return getStringProperty("datanucleus.ConnectionFactoryName");
    }

    /**
     * Get the name for the second data store connection factory.  This is
     * needed for managed environments to get nontransactional connections for
     * optimistic transactions.
     * @return the name of the data store connection factory.
     */
    public String getConnectionFactory2Name()
    {
        return getStringProperty("datanucleus.ConnectionFactory2Name");
    }

    /**
     * Get the data store connection factory.
     * @return the data store connection factory.
     */
    public Object getConnectionFactory()
    {
        return getProperty("datanucleus.ConnectionFactory");
    }

    /**
     * Get the second data store connection factory.  This is
     * needed for managed environments to get nontransactional connections for
     * optimistic transactions.
     * @return the data store connection factory.
     */
    public Object getConnectionFactory2()
    {
        return getProperty("datanucleus.ConnectionFactory2");
    }

    /**
     * Get the default Multithreaded setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @return the default Multithreaded setting.
     */
    public boolean getMultithreaded()
    {
        return getBooleanProperty("datanucleus.Multithreaded");
    }

    /**
     * Get the default Optimistic setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @return the default Optimistic setting.
     */
    public boolean getOptimistic()
    {
        return getBooleanProperty("datanucleus.Optimistic");
    }

    /**
     * Get the default RetainValues setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @return the default RetainValues setting.
     */
    public boolean getRetainValues()
    {
        return getBooleanProperty("datanucleus.RetainValues");
    }

    /**
     * Get the default RestoreValues setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @return the default RestoreValues setting.
     */
    public boolean getRestoreValues()
    {
        return getBooleanProperty("datanucleus.RestoreValues");
    }

    /**
     * Get the default NontransactionalRead setting for all
     * <tt>PersistenceManager</tt> instances obtained from this factory.
     * @return the default NontransactionalRead setting.
     */
    public boolean getNontransactionalRead()
    {
        return getBooleanProperty("datanucleus.NontransactionalRead");
    }

    /**
     * Get the default NontransactionalWrite setting for all
     * <tt>PersistenceManager</tt> instances obtained from this factory.
     * @return the default NontransactionalWrite setting.
     */
    public boolean getNontransactionalWrite()
    {
        return getBooleanProperty("datanucleus.NontransactionalWrite");
    }

    /**
     * Get the default IgnoreCache setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @return the IgnoreCache setting.
     */
    public boolean getIgnoreCache()
    {
        return getBooleanProperty("datanucleus.IgnoreCache");
    }

    /**
     * Accessor for the DetachAllOnCommit setting.
     * @return the DetachAllOnCommit setting.
     */
    public boolean getDetachAllOnCommit()
    {
        return getBooleanProperty("datanucleus.DetachAllOnCommit");
    }

    /**
     * Accessor for the CopyOnAttach setting.
     * @return the CopyOnAttach setting.
     */
    public boolean getCopyOnAttach()
    {
        return getBooleanProperty("datanucleus.CopyOnAttach");
    }

    /**
     * Get the name for any mapping, used in retrieving metadata files for ORM/Query data.
     * @return the name for the mapping.
     */
    public String getMapping()
    {
        return getStringProperty("datanucleus.Mapping");
    }

    /**
     * Accessor for the catalog to use for this persistence factory.
     * @return the name of the catalog
     */
    public String getCatalog()
    {
        return getStringProperty("datanucleus.mapping.Catalog");
    }

    /**
     * Accessor for the schema to use for this persistence factory.
     * @return the name of the schema
     */
    public String getSchema()
    {
        return getStringProperty("datanucleus.mapping.Schema");
    }

    /**
     * Accessor for the name of the persistence factory (if any).
     * @return the name of the persistence factory
     */
    public String getName()
    {
        return getStringProperty("datanucleus.Name");
    }

    /**
     * Accessor for the name of the persistence unit
     * @return the name of the persistence unit
     */
    public String getPersistenceUnitName()
    {
        return getStringProperty("datanucleus.PersistenceUnitName");
    }

    /**
     * Accessor for the filename of the persistence.xml file.
     * This is for the case where an application has placed the persistence.xml somewhere else maybe
     * outside the CLASSPATH.
     * @return the filename of the persistence unit
     */
    public String getPersistenceXmlFilename()
    {
        return getStringProperty("datanucleus.persistenceXmlFilename");
    }

    /**
     * Accessor for the query timeout interval.
     * @return query timeout
     */
    public Integer getQueryTimeoutMillis()
    {
        return getIntProperty("datanucleus.query.timeout");
    }

    /**
     * Accessor for the timezone "id" of the datastore server (if any).
     * If not set assumes the same as the JVM we are running in.
     * @return Server timezone id
     */
    public String getServerTimeZoneID()
    {
        return getStringProperty("datanucleus.ServerTimeZoneID");
    }

    /**
     * Get the default readOnly setting for all <tt>PersistenceManager</tt>
     * instances obtained from this factory.
     * @return the default readOnly setting.
     */
    public boolean getReadOnly()
    {
        return getBooleanProperty("datanucleus.ReadOnly");
    }

    /**
     * Accessor for the transaction type to use with this persistence factory.
     * @return transaction type
     */
    public String getTransactionType()
    {
        return getStringProperty("datanucleus.TransactionType");
    }

    /**
     * Accessor for the transaction isolation level default.
     * @return Transaction isolation level
     */
    public String getTransactionIsolationLevel()
    {
        return getStringProperty("datanucleus.transactionIsolation");
    }

    /**
     * Asserts that a change to a configuration property is allowed.
     */
    protected void assertConfigurable()
    {
        if (!configurable)
        {
            throw new JDOUserException(LOCALISER.msg("008016"));
        }
    }

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

    /**
     * @return Returns either <tt>null</tt> or a <tt>List</tt> with instances of
     *      <tt>LifecycleListenerSpecification</tt>.
     */
    public List<LifecycleListenerForClass> getLifecycleListenerSpecifications()
    {
        return lifecycleListeners;
    }

    /**
     * Method to add lifecycle listeners for particular classes.
     * Adds the listener to all PMs already created.
     * @param listener The listener
     * @param classes The classes that the listener relates to
     */
    public void addInstanceLifecycleListener(InstanceLifecycleListener listener, Class[] classes)
    {
        if (listener == null)
        {
            return;
        }

        synchronized (pmCache)
        {
            // Add to any PMs - make sure that the PM Cache isnt changed while doing so
            Iterator<JDOPersistenceManager> pms = getPmCache().iterator();
            while (pms.hasNext())
            {
                JDOPersistenceManager pm = pms.next();
                pm.addInstanceLifecycleListener(listener, classes);
            }
        }

        // Register the lifecycle listener
        if (lifecycleListeners == null)
        {
            lifecycleListeners = new ArrayList<LifecycleListenerForClass>(5);
        }
        lifecycleListeners.add(new LifecycleListenerForClass(listener, classes));
    }

    /**
     * Method to remove a lifecycle listener. Removes the listener from all PM's as well.
     * @param listener The Listener
     */
    public void removeInstanceLifecycleListener(InstanceLifecycleListener listener)
    {
        if (listener == null || lifecycleListeners == null)
        {
            return;
        }

        synchronized (pmCache)
        {
            // Remove from any PMs - make sure that the PM Cache isnt changed while doing so
            Iterator<JDOPersistenceManager> pms = getPmCache().iterator();
            while (pms.hasNext())
            {
                JDOPersistenceManager pm = pms.next();
                pm.removeInstanceLifecycleListener(listener);
            }
        }

        // Remove from the PMF
        Iterator<LifecycleListenerForClass> iter = lifecycleListeners.iterator();
        while (iter.hasNext())
        {
            LifecycleListenerForClass classListener = iter.next();
            if (classListener.getListener() == listener)
            {
                iter.remove();
            }
        }
    }

    // --------------------------- Sequences ----------------------------------

    /**
     * Method to register a sequence for a particular factory class.
     * @param factoryClassName Name of the factory class
     * @param sequence The sequence
     */
    public void addSequenceForFactoryClass(String factoryClassName, Sequence sequence)
    {
        if (sequenceByFactoryClass == null)
        {
            sequenceByFactoryClass = new HashMap();
        }

        sequenceByFactoryClass.put(factoryClassName, sequence);
    }

    /**
     * Accessor for the sequence for a factory class.
     * @param factoryClassName The name of the factory class
     * @return The sequence
     */
    public Sequence getSequenceForFactoryClass(String factoryClassName)
    {
        if (sequenceByFactoryClass == null)
        {
            return null;
        }

        return sequenceByFactoryClass.get(factoryClassName);
    }

    // --------------------------- Fetch Groups ----------------------------------

    /* (non-Javadoc)
     * @see javax.jdo.PersistenceManagerFactory#getFetchGroups()
     */
    public Set<JDOFetchGroup> getFetchGroups()
    {
        if (jdoFetchGroups == null)
        {
            return null;
        }

        // TODO Should return a copy
        return jdoFetchGroups;
    }

    /* (non-Javadoc)
     * @see javax.jdo.PersistenceManagerFactory#getFetchGroup(java.lang.Class, java.lang.String)
     */
    public javax.jdo.FetchGroup getFetchGroup(Class cls, String name)
    {
        if (jdoFetchGroups == null)
        {
            jdoFetchGroups = new HashSet();
        }

        synchronized (jdoFetchGroups)
        {
            Iterator<JDOFetchGroup> iter = jdoFetchGroups.iterator();
            while (iter.hasNext())
            {
                JDOFetchGroup jdoGrp = iter.next();
                if (jdoGrp.getName().equals(name) && jdoGrp.getType() == cls)
                {
                    return jdoGrp;
                }
            }
        }

        // Create new FetchGroup, but don't add to set of groups yet - user should add via addFetchGroups()
        try
        {
            org.datanucleus.FetchGroup internalGrp = getInternalFetchGroup(cls, name);
            if (!internalGrp.isUnmodifiable())
            {
                // Return existing internal group since still modifiable
                return new JDOFetchGroup(internalGrp);
            }
            else
            {
                // Create a new internal group (modifiable) and return a JDO group based on that
                internalGrp = createInternalFetchGroup(cls, name);
                addInternalFetchGroup(internalGrp);
                JDOFetchGroup jdoGrp = new JDOFetchGroup(internalGrp);
                return jdoGrp;
            }
        }
        catch (NucleusException ne)
        {
            throw NucleusJDOHelper.getJDOExceptionForNucleusException(ne);
        }
    }

    /* (non-Javadoc)
     * @see javax.jdo.PersistenceManagerFactory#addFetchGroups(javax.jdo.FetchGroup[])
     */
    public void addFetchGroups(FetchGroup... groups)
    {
        if (groups == null || groups.length == 0)
        {
            return;
        }

        if (jdoFetchGroups == null)
        {
            jdoFetchGroups = new HashSet<JDOFetchGroup>();
        }
        synchronized (jdoFetchGroups)
        {
            for (int i=0;i<groups.length;i++)
            {
                addInternalFetchGroup(((JDOFetchGroup)groups[i]).getInternalFetchGroup());
                jdoFetchGroups.add((JDOFetchGroup)groups[i]);
            }
        }
    }

    /* (non-Javadoc)
     * @see javax.jdo.PersistenceManagerFactory#removeFetchGroups(javax.jdo.FetchGroup[])
     */
    public void removeFetchGroups(FetchGroup... groups)
    {
        if (groups == null || groups.length == 0 || jdoFetchGroups == null)
        {
            return;
        }

        synchronized (jdoFetchGroups)
        {
            for (int i=0;i<groups.length;i++)
            {
                JDOFetchGroup jdoGrp = (JDOFetchGroup)groups[i];
                removeInternalFetchGroup(jdoGrp.getInternalFetchGroup());
                jdoFetchGroups.remove(jdoGrp);
            }
        }
    }

    /* (non-Javadoc)
     * @see javax.jdo.PersistenceManagerFactory#removeAllFetchGroups()
     */
    public void removeAllFetchGroups()
    {
        if (jdoFetchGroups != null)
        {
            synchronized (jdoFetchGroups)
            {
                Iterator<JDOFetchGroup> iter = jdoFetchGroups.iterator();
                while (iter.hasNext())
                {
                    JDOFetchGroup jdoGrp = iter.next();
                    removeInternalFetchGroup(jdoGrp.getInternalFetchGroup());
                }
                jdoFetchGroups.clear();
                jdoFetchGroups = null;
            }
        }
    }

    /**
     * Method to return a new metadata object that can be subsequently modified
     * and registered with the persistence process using the method {@link #registerMetadata}.
     * @return Metadata object to start from
     */
    public javax.jdo.metadata.JDOMetadata newMetadata()
    {
        return new JDOMetadataImpl();
    }

    /**
     * Method to register the supplied metadata with the persistence process managed by this
     * <code>PersistenceManagerFactory</code>.
     * Metadata can be created using the method {@link #newMetadata}.
     * @param metadata The Metadata to register.
     */
    public void registerMetadata(javax.jdo.metadata.JDOMetadata metadata)
    {
        MetaDataManager mmgr = omfContext.getMetaDataManager();
        FileMetaData filemd = ((JDOMetadataImpl)metadata).getInternal();

        // Check if already defined
        for (int i=0;i<filemd.getNoOfPackages();i++)
        {
            PackageMetaData pmd = filemd.getPackage(i);
            for (int j=0;j<pmd.getNoOfClasses();j++)
            {
                ClassMetaData cmd = pmd.getClass(j);
                if (mmgr.hasMetaDataForClass(cmd.getFullClassName()))
                {
                    throw new JDOUserException("Cannot redefine metadata for " + cmd.getFullClassName());
                }
            }
            for (int j=0;j<pmd.getNoOfInterfaces();j++)
            {
                InterfaceMetaData imd = pmd.getInterface(j);
                if (mmgr.hasMetaDataForClass(imd.getFullClassName()))
                {
                    throw new JDOUserException("Cannot redefine metadata for " + imd.getFullClassName());
                }
            }
        }

        mmgr.loadUserMetaData(filemd, null);
    }

    /**
     * Method to return the (class) metadata object for the specified class, if there is
     * metadata defined for that class.
     * @return The metadata
     */
    public javax.jdo.metadata.TypeMetadata getMetadata(String className)
    {
        MetaDataManager mmgr = omfContext.getMetaDataManager();
        AbstractClassMetaData acmd = mmgr.getMetaDataForClass(className,
            omfContext.getClassLoaderResolver(null));
        if (acmd == null)
        {
            return null;
        }
        else
        {
            if (acmd instanceof ClassMetaData)
            {
                return new ClassMetadataImpl((ClassMetaData)acmd);
            }
            else
            {
                return new InterfaceMetadataImpl((InterfaceMetaData)acmd);
            }
        }
    }
}
TOP

Related Classes of org.datanucleus.jdo.JDOPersistenceManagerFactory

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.