Package org.jpox.store

Source Code of org.jpox.store.AbstractStoreManager

/**********************************************************************
Copyright (c) 2004 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:
2007 Xuan Baldauf - Contrib of notifyMainMemoryCopyIsInvalid(), findObject() (needed by DB4O plugin).
    ...
**********************************************************************/
package org.jpox.store;

import java.io.PrintStream;
import java.io.Writer;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.jpox.ClassLoaderResolver;
import org.jpox.ConnectionFactory;
import org.jpox.ConnectionFactoryRegistry;
import org.jpox.ManagedConnection;
import org.jpox.OMFContext;
import org.jpox.ObjectManager;
import org.jpox.ObjectManagerFactoryImpl;
import org.jpox.PersistenceConfiguration;
import org.jpox.StateManager;
import org.jpox.api.ApiAdapter;
import org.jpox.exceptions.ClassNotResolvedException;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXOptimisticException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.identity.OID;
import org.jpox.identity.SCOID;
import org.jpox.jdo.JDOSequenceImpl;
import org.jpox.management.ManagementServer;
import org.jpox.management.runtime.StoreManagerRuntime;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ExtensionMetaData;
import org.jpox.metadata.IdentityStrategy;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.MetaDataManager;
import org.jpox.metadata.SequenceMetaData;
import org.jpox.metadata.TableGeneratorMetaData;
import org.jpox.metadata.VersionMetaData;
import org.jpox.metadata.VersionStrategy;
import org.jpox.plugin.ConfigurationElement;
import org.jpox.store.exceptions.DatastoreInitialisationException;
import org.jpox.store.poid.PoidGenerator;
import org.jpox.store.poid.PoidManager;
import org.jpox.store.scostore.Store;
import org.jpox.util.ClassUtils;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.StringUtils;
import org.jpox.util.TypeConversionHelper;

/**
* An abstract representation of a Store Manager.
* Manages the persistence of objects to the store.
* Will be implemented for the type of datastore (RDBMS, ODBMS, OLAP) in question.
* The store manager's responsibilities include:
* <ul>
* <li>Creating and/or validating datastore tables according to the persistent classes being
* accessed by the application.</li>
* <li>Serving as the primary intermediary between StateManagers and the database.</li>
* <li>Serving as the base Extent and Query factory.</li>
* </ul>
* <p>
* A store manager's knowledge of its contents is typically not complete. It knows about
* the classes that it has encountered in its lifetime. The PersistenceManager can make the
* StoreManager aware of a class, and can check if the StoreManager knows about a particular class.
* The Auto-Start mechanism provides a way of inheriting knowledge from the last time the store was used.
*
* @version $Revision: 1.180 $
*/
public abstract class AbstractStoreManager implements StoreManager
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.store.Localisation",
        ObjectManagerFactoryImpl.class.getClassLoader());

    /** Key for this StoreManager e.g "rdbms", "db4o" */
    protected final String storeManagerKey;

    /** Whether this datastore is read only. */
    protected final boolean readOnlyDatastore;

    /** What should happen if read-only and an update is invoked. */
    protected final String readOnlyDatastoreAction;

    /** Whether this datastore is fixed (no mods to datastore classes allowed). */
    protected final boolean fixedDatastore;

    /** Auto-Start mechanism to use. */
    protected AutoStartMechanism starter = null;

    /** Whether the AutoStart mechanism is initialised */
    protected boolean starterInitialised = false;

    /** ObjectManagerFactory context. */
    protected final OMFContext omfContext;

    /** Manager for identity generation. */
    protected final PoidManager poidManager;

    /** StoreManager Runtime. Used when providing management of services. */
    protected StoreManagerRuntime storeManagerRuntime;

    /** Manager for the data definition in the datastore. */
    protected StoreDataManager storeDataMgr = new StoreDataManager();

    /** Name of the AutoStart mechanism. */
    protected String autoStartMechanism = null;

    /** Persistence handler. */
    protected StorePersistenceHandler persistenceHandler = null;

    /** Schema handler. */
    protected StoreSchemaHandler schemaHandler = null;

    /** Name of transactional connection factory. */
    protected String txConnectionFactoryName;

    /** Name of non-transactional connection factory (null if not present). */
    protected String nontxConnectionFactoryName;

    /**
     * Constructor for a new StoreManager. Stores the basic information required for the datastore management.
     * @param key Key for this StoreManager
     * @param clr the ClassLoaderResolver
     * @param omfContext The corresponding ObjectManagerFactory context.
     * @see StoreManagerFactory
     */
    protected AbstractStoreManager(String key, ClassLoaderResolver clr, OMFContext omfContext)
    {
        this.storeManagerKey = key;
        this.omfContext = omfContext;
        PersistenceConfiguration conf = omfContext.getPersistenceConfiguration();
        this.readOnlyDatastore = conf.getBooleanProperty("org.jpox.readOnlyDatastore");
        this.readOnlyDatastoreAction = conf.getStringProperty("org.jpox.readOnlyDatastoreAction");
        this.fixedDatastore = conf.getBooleanProperty("org.jpox.fixedDatastore");
        this.autoStartMechanism = conf.getStringProperty("org.jpox.autoStartMechanism");

        // Register this StoreManager with the OMFContext
        omfContext.setStoreManager(this);

        this.poidManager = new PoidManager();

        if (omfContext.getManagement() != null)
        {
            // register MBean in MbeanServer
            ManagementServer mgntServer = omfContext.getManagement().getManagementServer();
            this.storeManagerRuntime = new StoreManagerRuntime();
            String mbeanName = omfContext.getDomainName() + ":InstanceName=" + omfContext.getInstanceName() +
                ",Type=" + ClassUtils.getClassNameForClass(storeManagerRuntime.getClass()) +
                ",Name=StoreManagerRuntime";
            mgntServer.registerMBean(this.storeManagerRuntime, mbeanName);
        }

        // Factory for connections - transactional
        ConfigurationElement cfElem = omfContext.getPluginManager().getConfigurationElementForExtension(
            "org.jpox.store_connectionfactory",
            new String[] {"datastore", "transactional"}, new String[] {storeManagerKey, "true"});
        if (cfElem != null)
        {
            txConnectionFactoryName = cfElem.getAttribute("name");
            try
            {
                ConnectionFactory cf = (ConnectionFactory)omfContext.getPluginManager().createExecutableExtension(
                    "org.jpox.store_connectionfactory",
                    new String[] {"datastore", "transactional"},
                    new String[] {storeManagerKey, "true"}, "class-name",
                    new Class[] {OMFContext.class, String.class},
                    new Object[] {omfContext, "tx"});
                omfContext.getConnectionFactoryRegistry().registerConnectionFactory(txConnectionFactoryName, cf);
                if (JPOXLogger.CONNECTION.isDebugEnabled())
                {
                    JPOXLogger.CONNECTION.debug(LOCALISER.msg("032018", txConnectionFactoryName));
                }
            }
            catch (Exception e)
            {
                throw new JPOXException("Error creating transactional connection factory", e).setFatal();
            }
        }
        else
        {
            throw new JPOXException("Error creating transactional connection factory. No connection factory plugin defined");
        }

        // Factory for connections - nontransactional
        cfElem = omfContext.getPluginManager().getConfigurationElementForExtension(
            "org.jpox.store_connectionfactory",
            new String[] {"datastore", "transactional"}, new String[] {storeManagerKey, "false"});
        if (cfElem != null)
        {
            nontxConnectionFactoryName = cfElem.getAttribute("name");
            try
            {
                ConnectionFactory cf = (ConnectionFactory)omfContext.getPluginManager().createExecutableExtension(
                    "org.jpox.store_connectionfactory",
                    new String[] {"datastore", "transactional"},
                    new String[] {storeManagerKey, "false"}, "class-name",
                    new Class[] {OMFContext.class, String.class},
                    new Object[] {omfContext, "nontx"});
                if (JPOXLogger.CONNECTION.isDebugEnabled())
                {
                    JPOXLogger.CONNECTION.debug(LOCALISER.msg("032019", nontxConnectionFactoryName));
                }
                omfContext.getConnectionFactoryRegistry().registerConnectionFactory(nontxConnectionFactoryName, cf);
            }
            catch (Exception e)
            {
                throw new JPOXException("Error creating nontransactional connection factory", e).setFatal();
            }
        }
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#close()
     */
    public void close()
    {
        poidManager.clear();
        storeDataMgr.clear();
        starterInitialised = false;
        starter = null;
        if (persistenceHandler != null)
        {
            persistenceHandler.close();
            persistenceHandler = null;
        }
    }

    /**
     * Accessor for a transactional connection for the specified ObjectManager, using its current transaction.
     * @param om The ObjectManager
     * @return The Connection
     * @throws JPOXException Thrown if an error occurs getting the connection
     */
    public ManagedConnection getConnection(ObjectManager om)
    {
        ConnectionFactoryRegistry registry = om.getOMFContext().getConnectionFactoryRegistry();
        ConnectionFactory connFactory = registry.lookupConnectionFactory(txConnectionFactoryName);
        return connFactory.getConnection(om, null);
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getRuntimeManager()
     */
    public StoreManagerRuntime getRuntimeManager()
    {
        return storeManagerRuntime;
    }

  /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getPersistenceHandler()
     */
    public StorePersistenceHandler getPersistenceHandler()
    {
        return persistenceHandler;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getMetaDataHandler()
     */
    public StoreSchemaHandler getSchemaHandler()
    {
        return schemaHandler;
    }

    /**
     * Method to return a datastore sequence for this datastore matching the passed sequence MetaData.
     * @param om The Object Manager
     * @param seqmd SequenceMetaData
     * @return The Sequence
     */
    public JPOXSequence getJPOXSequence(ObjectManager om, SequenceMetaData seqmd)
    {
        return new JDOSequenceImpl(om, this, seqmd);
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getJPOXConnection(org.jpox.ObjectManager)
     */
    public abstract JPOXConnection getJPOXConnection(ObjectManager om);

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getPoidManager()
     */
    public PoidManager getPoidManager()
    {
        return poidManager;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getApiAdapter()
     */
    public ApiAdapter getApiAdapter()
    {
        return omfContext.getApiAdapter();
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getStoreManagerKey()
     */
    public String getStoreManagerKey()
    {
        return storeManagerKey;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getOMFContext()
     */  
    public OMFContext getOMFContext()
    {
        return omfContext;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getMetaDataManager()
     */  
    public MetaDataManager getMetaDataManager()
    {
        return omfContext.getMetaDataManager();
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getDatastoreDate()
     */
    public abstract Date getDatastoreDate();

    // -------------------------------- Management of Classes --------------------------------

    /**
     * Method to register some data with the store.
     * This will also register the data with the starter process.
     * @param data The StoreData to add
     */
    protected void registerStoreData(StoreData data)
    {
        storeDataMgr.registerStoreData(data);

        // Keep the AutoStarter in step with our managed classes/fields
        if (starter != null && starterInitialised)
        {
            starter.addClass(data);
        }
    }

    /**
     * Method to deregister all existing store data so that we are managing nothing.
     */
    protected void deregisterAllStoreData()
    {
        storeDataMgr.clear();
        starterInitialised = false;

        // Keep the AutoStarter in step with our managed classes/fields
        clearAutoStarter();
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#outputDatastoreInformation(java.io.PrintStream)
     */
    public abstract void outputDatastoreInformation(PrintStream ps)
    throws Exception;

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#outputSchemaInformation(java.io.PrintStream)
     */
    public abstract void outputSchemaInformation(PrintStream ps)
    throws Exception;

    // -------------------------------- Auto Starter ------------------------------------

    /**
     * Method to initialise the auto-start mechanism, loading up the classes
     * from its store into memory so that we start from where we got to last time.
     * @param mechanism Auto-Start mechanism
     * @param mode Mode of operation
     * @param clr The ClassLoaderResolver
     * @throws DatastoreInitialisationException
     */
    protected void initialiseAutoStart(String mechanism, String mode, ClassLoaderResolver clr)
    throws DatastoreInitialisationException
    {
        if (starterInitialised)
        {
            return;
        }

        autoStartMechanism = mechanism;

        String autoStarterClassName =
            getOMFContext().getPluginManager().getAttributeValueForExtension("org.jpox.autostart",
                "name", mechanism, "class-name");
        if (autoStarterClassName != null)
        {
            Class[] argsClass = new Class[] {org.jpox.store.StoreManager.class, org.jpox.ClassLoaderResolver.class};
            Object[] args = new Object[] {this, clr};
            try
            {
                starter = (AutoStartMechanism) getOMFContext().getPluginManager().createExecutableExtension("org.jpox.autostart", "name", mechanism, "class-name", argsClass, args);
            }
            catch (Exception e)
            {
                JPOXLogger.PERSISTENCE.error(StringUtils.getStringFromStackTrace(e));
            }
            starter.setMode(mode);
        }
        if (starter == null)
        {
            starterInitialised = true;
            return;
        }

        boolean illegalState = false;
        try
        {
            if (!starter.isOpen())
            {
                starter.open();
            }
            Collection existingData = starter.getAllClassData();
            if (existingData != null && existingData.size() > 0)
            {
                List classesNeedingAdding = new ArrayList();
                Iterator existingDataIter = existingData.iterator();
                while (existingDataIter.hasNext())
                {
                    StoreData data = (StoreData) existingDataIter.next();
                    if (data.isFCO())
                    {
                        // Catch classes that don't exist (e.g in use by a different app)
                        Class classFound = null;
                        try
                        {
                            classFound = clr.classForName(data.getName());
                        }
                        catch (ClassNotResolvedException cnre)
                        {
                            if (data.getInterfaceName() != null)
                            {
                                try
                                {
                                    getOMFContext().getImplementationCreator().newInstance(clr.classForName(data.getInterfaceName()),
                                        getMetaDataManager(), clr);
                                    classFound = clr.classForName(data.getName());
                                }
                                catch (ClassNotResolvedException cnre2)
                                {
                                    // Do nothing
                                }
                            }
                            // Thrown if class not found
                        }

                        if (classFound != null)
                        {
                            JPOXLogger.PERSISTENCE.info(LOCALISER.msg("032003", data.getName()));
                            classesNeedingAdding.add(data.getName());
                            if (data.getMetaData() == null)
                            {
                                // StoreData doesnt have its metadata set yet so load it
                                // This ensures that the MetaDataManager always knows about these classes
                                AbstractClassMetaData acmd =
                                    getMetaDataManager().getMetaDataForClass(classFound, clr);
                                if (acmd != null)
                                {
                                    data.setMetaData(acmd);
                                }
                                else
                                {
                                    String msg = LOCALISER.msg("034004", data.getName());
                                    if (starter.getMode().equals(AutoStartMechanism.MODE_CHECKED))
                                    {
                                        JPOXLogger.PERSISTENCE.error(msg);
                                        throw new DatastoreInitialisationException(msg);
                                    }
                                    else if (starter.getMode().equals(AutoStartMechanism.MODE_IGNORED))
                                    {
                                        JPOXLogger.PERSISTENCE.warn(msg);
                                    }
                                    else if (starter.getMode().equals(AutoStartMechanism.MODE_QUIET))
                                    {
                                        JPOXLogger.PERSISTENCE.warn(msg);
                                        JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("034001", data.getName()));
                                        starter.deleteClass(data.getName());
                                    }
                                }
                            }
                        }
                        else
                        {
                            String msg = LOCALISER.msg("034000", data.getName());
                            if (starter.getMode().equals(AutoStartMechanism.MODE_CHECKED))
                            {
                                JPOXLogger.PERSISTENCE.error(msg);
                                throw new DatastoreInitialisationException(msg);
                            }
                            else if (starter.getMode().equals(AutoStartMechanism.MODE_IGNORED))
                            {
                                JPOXLogger.PERSISTENCE.warn(msg);
                            }
                            else if (starter.getMode().equals(AutoStartMechanism.MODE_QUIET))
                            {
                                JPOXLogger.PERSISTENCE.warn(msg);
                                JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("034001", data.getName()));
                                starter.deleteClass(data.getName());
                            }
                        }
                    }
                }
                String[] classesToLoad = new String[classesNeedingAdding.size()];
                Iterator classesNeedingAddingIter = classesNeedingAdding.iterator();
                int n = 0;
                while (classesNeedingAddingIter.hasNext())
                {
                    classesToLoad[n++] = (String)classesNeedingAddingIter.next();
                }

                // Load the classes
                try
                {
                    addClasses(classesToLoad, clr);
                }
                catch (Exception e)
                {
                    // Exception while adding so some of the (referenced) classes dont exist
                    JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("034002", e));
                    //if an exception happens while loading AutoStart data, them we disable it, since
                    //it was unable to load the data from AutoStart. The memory state of AutoStart does
                    //not represent the database, and if we don't disable it, we could
                    //think that the autostart store is empty, and we would try to insert new entries in
                    //the autostart store that are already in there
                    illegalState = true;

                    // TODO Go back and add classes one-by-one to eliminate the class(es) with the problem
                }
            }
        }
        finally
        {
            if (starter.isOpen())
            {
                starter.close();
            }
            if (illegalState)
            {
                JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("034003"));
                starter = null;
            }
        }

        starterInitialised = true;
    }

    /**
     * Method to clear the Auto-Starter status.
     */
    protected void clearAutoStarter()
    {
        // AutoStarter - Delete all supported classes
        if (starter != null)
        {
            try
            {
                if (!starter.isOpen())
                {
                    starter.open();
                }
                starter.deleteAllClasses();
            }
            finally
            {
                if (starter.isOpen())
                {
                    starter.close();
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getAutoStartMechanism()
     */
    public AutoStartMechanism getAutoStartMechanism()
    {
        return starter;
    }
   
    // ------------------------------- Class Management -----------------------------------

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#managesClass(java.lang.String)
     */
    public boolean managesClass(String className)
    {
        return storeDataMgr.managesClass(className);
    }
   
    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#addClass(java.lang.String, org.jpox.ClassLoaderResolver)
     */
    public void addClass(String className, ClassLoaderResolver clr)
    {
        addClasses(new String[] {className}, clr, null, false);
    }
   
    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#addClasses(java.lang.String[], org.jpox.ClassLoaderResolver, java.io.Writer, boolean)
     */
    public abstract void addClasses(String[] classes, ClassLoaderResolver clr, Writer writer, boolean completeDdl);

   /* (non-Javadoc)
* @see org.jpox.store.StoreManager#addClasses(java.lang.String[], org.jpox.ClassLoaderResolver)
*/
     public void addClasses(String[] classNames, ClassLoaderResolver clr)
     {
         addClasses(classNames, clr, null, false);
     }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#removeAllClasses(org.jpox.ClassLoaderResolver)
     */
    public abstract void removeAllClasses(ClassLoaderResolver clr);

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#manageClassForIdentity(java.lang.Object, org.jpox.ClassLoaderResolver)
     */
    public String manageClassForIdentity(Object id, ClassLoaderResolver clr)
    {
        String className = null;

        if (id instanceof OID)
        {
            // Check that the implied class is managed
            className = ((OID)id).getPcClass();
            AbstractClassMetaData cmd = getMetaDataManager().getMetaDataForClass(className, clr);
            if (cmd.getIdentityType() != IdentityType.DATASTORE)
            {
                throw new JPOXUserException(LOCALISER.msg("038001", id, cmd.getFullClassName()));
            }

        }
        else if (getApiAdapter().isSingleFieldIdentity(id))
        {
            className = getApiAdapter().getTargetClassNameForSingleFieldIdentity(id);
            AbstractClassMetaData cmd = getMetaDataManager().getMetaDataForClass(className, clr);
            if (cmd.getIdentityType() != IdentityType.APPLICATION || !cmd.getObjectidClass().equals(id.getClass().getName()))
            {
                throw new JPOXUserException(LOCALISER.msg("038001", id, cmd.getFullClassName()));
            }
        }
        else
        {
            throw new JPOXException("StoreManager.manageClassForIdentity called for id=" + id +
                " yet should only be called for datastore-identity/SingleFieldIdentity");
        }

        // If the class is not yet managed, manage it
        if (!managesClass(className))
        {
            addClass(className, clr);
        }

        return className;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#usesDatastoreClass()
     */
    public boolean usesDatastoreClass()
    {
        return false;
    }

    // ------------------------------ PersistenceManager interface -----------------------------------

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getExtent(org.jpox.ObjectManager, java.lang.Class, boolean)
     */
    public abstract Extent getExtent(ObjectManager om, Class c, boolean subclasses);

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#supportsQueryLanguage(java.lang.String)
     */
    public boolean supportsQueryLanguage(String language)
    {
        if (language == null)
        {
            return false;
        }

        // Find if datastore=storeManagerKey has an extension for name="{language}"
        String name = getOMFContext().getPluginManager().getAttributeValueForExtension("org.jpox.store_query_query",
            new String[] {"name", "datastore"}, new String[]{language, storeManagerKey}, "name");
        return (name != null);
    }

    /**
     * Accessor for whether this value strategy is supported.
     * @param strategy The strategy
     * @return Whether it is supported.
     */
    public boolean supportsValueStrategy(String strategy)
    {
        ConfigurationElement elem =
            omfContext.getPluginManager().getConfigurationElementForExtension(
                "org.jpox.store_valuegenerator",
                new String[]{"name", "unique"},
                new String[] {strategy, "true"});
        if (elem != null)
        {
            // Unique strategy so supported for all datastores
            return true;
        }
        else
        {
            elem = omfContext.getPluginManager().getConfigurationElementForExtension(
                "org.jpox.store_valuegenerator",
                new String[]{"name", "datastore"},
                new String[] {strategy, storeManagerKey});
            if (elem != null)
            {
                return true;
            }
        }
        return false;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getClassNameForObjectID(java.lang.Object, org.jpox.ClassLoaderResolver, org.jpox.ObjectManager)
     */
    public String getClassNameForObjectID(Object id, ClassLoaderResolver clr, ObjectManager om)
    {
        if (id instanceof SCOID)
        {
            // Object is a SCOID
            return ((SCOID) id).getSCOClass();
        }
        else if (id instanceof OID)
        {
            // Object is an OID
            return ((OID)id).getPcClass();
        }
        else if (getApiAdapter().isSingleFieldIdentity(id))
        {
            // Using SingleFieldIdentity so can assume that object is of the target class
            return getApiAdapter().getTargetClassNameForSingleFieldIdentity(id);
        }
        else
        {
            // Application identity with user PK class
            // Find all of the application identity PK classes of this type
            Collection c = storeDataMgr.getByPrimaryKeyClass(id.getClass().getName());
            if (c != null)
            {
                Iterator iter = c.iterator();
                while (iter.hasNext())
                {
                    // Just return the class name of the first one using this id - could be any in this tree
                    // TODO Provide a way of finding the precise type - e.g cont.get(object with this id)
                    StoreData store_data = (StoreData)iter.next();
                    return store_data.getName();
                }
            }
            return null;
        }
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#isStrategyDatastoreAttributed(org.jpox.metadata.IdentityStrategy, boolean)
     */
    public boolean isStrategyDatastoreAttributed(IdentityStrategy identityStrategy, boolean datastoreIdentityField)
    {
        if (identityStrategy == null)
        {
            return false;
        }

        /*if (identityStrategy == IdentityStrategy.NATIVE && dba.supportsAutoIncrementFields())
        {
            // If the user leaves it to us and the DBA supports autoincrement then we use it
            // which means attributing the id in the datastore.
            return true;
        }*/

        // "identity" is processed in the datastore
        if (identityStrategy == IdentityStrategy.IDENTITY)
        {
            // TODO "identity" could imply using a SEQUENCE, so we need to allow for this here
            return true;
        }

        return false;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getStrategyValue(org.jpox.ObjectManager, org.jpox.metadata.AbstractClassMetaData, int)
     */
    public Object getStrategyValue(ObjectManager om, AbstractClassMetaData cmd, int absoluteFieldNumber)
    {
        // Extract the control information for this field that needs its value
        AbstractMemberMetaData mmd = null;
        String fieldName = null;
        IdentityStrategy strategy = null;
        String sequence = null;
        String valueGeneratorName = null;
        TableGeneratorMetaData tableGeneratorMetaData = null;
        SequenceMetaData sequenceMetaData = null;
        if (absoluteFieldNumber >= 0)
        {
            // real field
            mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(absoluteFieldNumber);
            fieldName = mmd.getFullFieldName();
            strategy = mmd.getValueStrategy();
            sequence = mmd.getSequence();
            valueGeneratorName = mmd.getValueGeneratorName();
        }
        else
        {
            // datastore-identity surrogate field
            fieldName = cmd.getFullClassName() + " (datastore id)";
            strategy = cmd.getIdentityMetaData().getValueStrategy();
            sequence = cmd.getIdentityMetaData().getSequence();
            valueGeneratorName = cmd.getIdentityMetaData().getValueGeneratorName();
        }

        // Extract any metadata-based generation information
        if (valueGeneratorName != null)
        {
            if (strategy == IdentityStrategy.INCREMENT)
            {
                tableGeneratorMetaData = getMetaDataManager().getMetaDataForTableGenerator(om.getClassLoaderResolver(),
                    valueGeneratorName);
                if (tableGeneratorMetaData == null)
                {
                    throw new JPOXUserException(LOCALISER.msg("038005", fieldName, valueGeneratorName));
                }
            }
            else if (strategy == IdentityStrategy.SEQUENCE)
            {
                sequenceMetaData = getMetaDataManager().getMetaDataForSequence(om.getClassLoaderResolver(),
                    valueGeneratorName);
                if (sequenceMetaData == null)
                {
                    throw new JPOXUserException(LOCALISER.msg("038006", fieldName, valueGeneratorName));
                }
            }
        }
        else if (strategy == IdentityStrategy.SEQUENCE && sequence != null)
        {
            // TODO Allow for package name of this class prefix for the sequence name
            sequenceMetaData = getMetaDataManager().getMetaDataForSequence(om.getClassLoaderResolver(), sequence);
            if (sequenceMetaData == null)
            {
                // JPOX 1.1 behaviour is just to use the sequence name in the datastore so log it and fallback to that
                JPOXLogger.DATASTORE.warn("Field " + fieldName + " has been specified to use sequence " + sequence +
                    " but there is no <sequence> specified in the MetaData. " +
                    "Falling back to use a sequence in the datastore with this name directly.");
            }
        }

        String strategyName = strategy.toString();
        if (strategy.equals(IdentityStrategy.CUSTOM))
        {
            // Using a "custom" generator
            strategyName = strategy.getCustomName();
        }
        else if (strategy.equals(IdentityStrategy.NATIVE))
        {
            strategyName = getStrategyForNative(sequence);
        }

        // POID Generators are cached against a name. Some POID Generators are unique per datastore.
        // Others are per class/field. Others have a user-defined name.
        // The name that they are cached under relates to what they use.
        // Generate the name for PoidManager to use the strategy for this field/class.
        String poidGeneratorName = null;
        String generatorNameKeyInManager = null;
        ConfigurationElement elem =
            omfContext.getPluginManager().getConfigurationElementForExtension("org.jpox.store_valuegenerator",
                new String[]{"name", "unique"}, new String[] {strategyName, "true"});
        if (elem != null)
        {
            // Unique value generator so set the key in PoidManager to the value generator name itself
            poidGeneratorName = elem.getAttribute("name");
            generatorNameKeyInManager = poidGeneratorName;
        }
        else
        {
            // Not a unique (datastore-independent) generator so try just for this datastore
            elem = omfContext.getPluginManager().getConfigurationElementForExtension("org.jpox.store_valuegenerator",
                new String[]{"name", "datastore"}, new String[] {strategyName, storeManagerKey});
            if (elem != null)
            {
                // Set the generator name (for use by the PoidManager)
                poidGeneratorName = elem.getAttribute("name");
            }
        }
        if (generatorNameKeyInManager == null)
        {
            // Value generator is not unique so set based on class/field
            if (absoluteFieldNumber >= 0)
            {
                // Require generation for a field so use field name for the generator symbolic name
                generatorNameKeyInManager = mmd.getFullFieldName();
            }
            else
            {
                // Require generation for a datastore id field so use the class name for the generator symbolic name
                generatorNameKeyInManager = cmd.getFullClassName();
            }
        }

        // Try to find the generator from the PoidManager if we already have it managed
        PoidGenerator generator = poidManager.getPoidGenerator(generatorNameKeyInManager);
        if (generator == null)
        {
            if (poidGeneratorName == null)
            {
                // No available value-generator for the specified strategy for this datastore
                throw new JPOXUserException(LOCALISER.msg("038004", strategy));
            }

            // Set up the default properties available for all value generators
            Properties props = getPropertiesForGenerator(cmd, absoluteFieldNumber, om, sequenceMetaData,
                tableGeneratorMetaData);

            Class cls = null;
            if (elem != null)
            {
                cls = omfContext.getPluginManager().loadClass(elem.getExtension().getPlugin().getSymbolicName(),
                    elem.getAttribute("class-name"));
            }
            if (cls == null)
            {
                throw new JPOXException("Cannot create Poid Generator for strategy " + poidGeneratorName);
            }
           
            // Create the new PoidGenerator since the first time required (registers it with the PoidManager too)
            generator = poidManager.createPoidGenerator(generatorNameKeyInManager, cls, props, this, null);
        }

        // Get the next value from the generator
        Object oid = getStrategyValueForGenerator(generator, om);

        if (mmd != null)
        {
            // replace the value of the id, but before convert the value to the field type if needed
            Object convertedValue = TypeConversionHelper.convertTo(oid, mmd.getType());
            if (convertedValue == null)
            {
                throw new JPOXException(LOCALISER.msg("038003", mmd.getFullFieldName(), oid)).setFatal();
            }
            oid = convertedValue;
        }

        if (JPOXLogger.POID.isDebugEnabled())
        {
            JPOXLogger.POID.debug(LOCALISER.msg("038002", fieldName, strategy, generator.getClass().getName(), oid));
        }

        return oid;
    }

    /**
     * Method defining which value-strategy to use when the user specifies "native".
     * @param sequence Sequence name if specified
     * @return Just returns "increment". Should be overridden by all store managers that have other behaviour.
     */
    protected String getStrategyForNative(String sequence)
    {
        return "increment";
    }

    /**
     * Accessor for the next value from the specified generator.
     * This implementation simply returns generator.next(). Any case where the generator requires
     * datastore connections should override this method.
     * @param generator The generator
     * @param om ObjectManager
     * @return The next value.
     */
    protected Object getStrategyValueForGenerator(PoidGenerator generator, ObjectManager om)
    {
        return generator.next();
    }

    /**
     * Method to return the properties to pass to the generator for the specified field.
     * @param cmd MetaData for the class
     * @param absoluteFieldNumber Number of the field (-1 = datastore identity)
     * @param om Object Manager
     * @param seqmd Any sequence metadata
     * @param tablegenmd Any table generator metadata
     * @return The properties to use for this field
     */
    protected Properties getPropertiesForGenerator(AbstractClassMetaData cmd, int absoluteFieldNumber,
            ObjectManager om, SequenceMetaData seqmd, TableGeneratorMetaData tablegenmd)
    {
        ExtensionMetaData[] extensions = null;
        if (absoluteFieldNumber >= 0)
        {
            // real field
            AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(absoluteFieldNumber);
            extensions = mmd.getExtensions();
        }
        else
        {
            // datastore-identity surrogate field
            extensions = cmd.getIdentityMetaData().getExtensions();
        }

        // Set up the default properties available for all value generators
        Properties properties = new Properties();
        properties.setProperty("class-name", cmd.getFullClassName());
        properties.put("root-class-name", cmd.getBaseAbstractClassMetaData().getFullClassName());
        if (cmd.getMetaDataForManagedMemberAtAbsolutePosition(absoluteFieldNumber) != null)
        {
            properties.setProperty("field-name",
                cmd.getMetaDataForManagedMemberAtAbsolutePosition(absoluteFieldNumber).getFullFieldName());
        }

        // Add any id extension properties to the value generator properties
        if (extensions != null)
        {
            for (int i=0;i<extensions.length;i++)
            {
                properties.put(extensions[i].getKey(), extensions[i].getValue());
            }
        }

        return properties;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#performVersionCheck(org.jpox.StateManager, java.lang.Object, org.jpox.metadata.VersionMetaData)
     */
    public void performVersionCheck(StateManager sm, Object versionDatastore, VersionMetaData versionMetaData)
    {
        // Extract the version of the object (that we are updating)
        Object versionObject = sm.getTransactionalVersion(sm.getObject());
        if (versionObject == null)
        {
            return;
        }

        if (versionMetaData == null)
        {
            // No version specification so no check needed
            JPOXLogger.JDO.info(sm.getClassMetaData().getFullClassName() +
                " has no version metadata so no check of version is required, since this will not have the version flag in its table");
            return;
        }

        boolean valid;
        if (versionMetaData.getVersionStrategy() == VersionStrategy.DATE_TIME)
        {
            valid = ((Timestamp)versionObject).getTime() == ((Timestamp)versionDatastore).getTime();
        }
        else if (versionMetaData.getVersionStrategy() == VersionStrategy.VERSION_NUMBER)
        {
            valid = ((Number)versionObject).longValue() == ((Number)versionDatastore).longValue();
        }
        else if (versionMetaData.getVersionStrategy() == VersionStrategy.STATE_IMAGE)
        {
            // TODO Support state-image strategy
            throw new JPOXUserException(LOCALISER.msg("032017",
                sm.getClassMetaData().getFullClassName(), versionMetaData.getVersionStrategy()));
        }
        else
        {
            throw new JPOXUserException(LOCALISER.msg("032017",
                sm.getClassMetaData().getFullClassName(), versionMetaData.getVersionStrategy()));
        }

        if (!valid)
        {
            String msg = LOCALISER.msg("032016",
                StringUtils.toJVMIDString(sm.getObject()), sm.getInternalObjectId(), "" + versionDatastore, "" + versionObject);
            JPOXLogger.PERSISTENCE.error(msg);
            throw new JPOXOptimisticException(msg, sm.getObject());
        }
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getSubClassesForClass(java.lang.String, boolean, org.jpox.ClassLoaderResolver)
     */
    public HashSet getSubClassesForClass(String className, boolean includeDescendents, ClassLoaderResolver clr)
    {
        HashSet subclasses = new HashSet();

        String[] subclassNames = getMetaDataManager().getSubclassesForClass(className, includeDescendents);
        if (subclassNames != null)
        {
            // Load up the table for any classes that are not yet loaded
            for (int i=0;i<subclassNames.length;i++)
            {
                if (!storeDataMgr.managesClass(subclassNames[i]))
                {
                    // We have no knowledge of this class so load it now
                    addClass(subclassNames[i], clr);
                }
                subclasses.add(subclassNames[i]);
            }
        }

        return subclasses;
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#notifyObjectIsOutdated(org.jpox.StateManager)
     */
    public void notifyObjectIsOutdated(StateManager sm)
    {       
    }

    /* (non-Javadoc)
     * @see org.jpox.store.StoreManager#getBackingStoreForField(org.jpox.ClassLoaderResolver, org.jpox.metadata.AbstractMemberMetaData, java.lang.Class)
     */
    public Store getBackingStoreForField(ClassLoaderResolver clr, AbstractMemberMetaData fmd, Class type)
    {
        throw new UnsupportedOperationException("Backing stores are not supported in this datastore");
    }
   
    /**
     * Accessor for the supported options in string form.
     */
    public Collection getSupportedOptions()
    {
        return Collections.EMPTY_SET;
    }
}
TOP

Related Classes of org.jpox.store.AbstractStoreManager

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.