Package org.jpox.store.rdbms

Source Code of org.jpox.store.rdbms.RDBMSPersistenceHandler

/**********************************************************************
Copyright (c) 2008 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:
    ...
**********************************************************************/
package org.jpox.store.rdbms;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.jpox.ClassLoaderResolver;
import org.jpox.ObjectManager;
import org.jpox.PersistenceConfiguration;
import org.jpox.StateManager;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXObjectNotFoundException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.InheritanceStrategy;
import org.jpox.store.StoreManager;
import org.jpox.store.StorePersistenceHandler;
import org.jpox.store.exceptions.DatastorePermissionException;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.rdbms.request.DeleteRequest;
import org.jpox.store.rdbms.request.FetchRequest;
import org.jpox.store.rdbms.request.InsertRequest;
import org.jpox.store.rdbms.request.LocateRequest;
import org.jpox.store.rdbms.request.RequestIdentifier;
import org.jpox.store.rdbms.request.UpdateRequest;
import org.jpox.store.rdbms.table.SecondaryTable;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.SoftValueMap;
import org.jpox.util.StringUtils;

/**
* Handler for persistence for RDBMS datastores.
* @version $Revision$
*/
public class RDBMSPersistenceHandler implements StorePersistenceHandler
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.store.Localisation",
        StoreManager.class.getClassLoader());

    /** Manager for the store. */
    protected final MappedStoreManager storeMgr;

    /** The cache of database requests. Access is synchronized on the map object itself. */
    private Map requestsByID = Collections.synchronizedMap(new SoftValueMap());

    /**
     * Constructor.
     * @param storeMgr StoreManager
     */
    public RDBMSPersistenceHandler(StoreManager storeMgr)
    {
        this.storeMgr = (MappedStoreManager)storeMgr;
    }

    /**
     * Method to close the handler and release any resources.
     */
    public void close()
    {
        requestsByID.clear();
        requestsByID = null;
    }

    // ------------------------------ Insert ----------------------------------

    /**
     * Inserts a persistent object into the database.
     * @param sm The state manager of the object to be inserted.
     * @throws JPOXDataStoreException when an error occurs in the datastore communication
     */
    public void insertObject(StateManager sm)
    {
        PersistenceConfiguration conf = storeMgr.getOMFContext().getPersistenceConfiguration();
        if (conf.getBooleanProperty("org.jpox.readOnlyDatastore"))
        {
            if (conf.getStringProperty("org.jpox.readOnlyDatastoreAction").equalsIgnoreCase("EXCEPTION"))
            {
                throw new DatastorePermissionException(LOCALISER.msg("032004",
                    StringUtils.toJVMIDString(sm.getObject())));
            }
            else
            {
                if (JPOXLogger.PERSISTENCE.isDebugEnabled())
                {
                    JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("032005",
                        StringUtils.toJVMIDString(sm.getObject())));
                }
                return;
            }
        }

        ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
        String className = sm.getObject().getClass().getName();
        DatastoreClass dc = storeMgr.getDatastoreClass(className, clr);
        if (dc == null)
        {
            AbstractClassMetaData cmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(className, clr);
            if (cmd.getInheritanceMetaData().getStrategyValue() == InheritanceStrategy.SUBCLASS_TABLE)
            {
                throw new JPOXUserException(LOCALISER.msg("032013", className));
            }
            throw new JPOXException(LOCALISER.msg("032014", className,
                cmd.getInheritanceMetaData().getStrategyValue())).setFatal();
        }

        if (storeMgr.getRuntimeManager() != null)
        {
            storeMgr.getRuntimeManager().incrementInsertCount();
        }

        insertTable(dc, sm, clr);
    }

    /**
     * Convenience method to handle the insert into the various tables that this object is persisted into.
     * @param table The table to process
     * @param sm StateManager for the object being inserted
     * @param clr ClassLoader resolver
     */
    private void insertTable(DatastoreClass table, StateManager sm, ClassLoaderResolver clr)
    {
        DatastoreClass supertable = table.getSuperDatastoreClass();
        if (supertable != null)
        {
            // Process the superclass table first
            insertTable(supertable, sm, clr);
        }

        // Do the actual insert of this table
        // TODO Assert if this table is not yet initialised ?
        InsertRequest req = getInsertRequest(table, sm.getObject().getClass(), clr);
        req.execute(sm);

        // Process any secondary tables
        Collection secondaryTables = table.getSecondaryDatastoreClasses();
        if (secondaryTables != null)
        {
            Iterator tablesIter = secondaryTables.iterator();
            while (tablesIter.hasNext())
            {
                // Process the secondary table
                SecondaryTable secTable = (SecondaryTable)tablesIter.next();
                insertTable(secTable, sm, clr);
            }
        }
    }

    /**
     * Returns a request object that will insert a row in the given table.
     * The store manager will cache the request object for re-use by subsequent requests to the same table.
     * @param table The table into which to insert.
     * @param cls class of the object of the request
     * @param clr ClassLoader resolver
     * @return An insertion request object.
     */
    private InsertRequest getInsertRequest(DatastoreClass table, Class cls, ClassLoaderResolver clr)
    {
        RequestIdentifier reqID = new RequestIdentifier(table, null, RequestIdentifier.Type.INSERT, cls.getName());
        InsertRequest req = (InsertRequest) requestsByID.get(reqID);
        if (req == null)
        {
            req = new InsertRequest(table, cls, clr);
            requestsByID.put(reqID, req);
        }
        return req;
    }

    // ------------------------------ Fetch ----------------------------------

    /**
     * Fetches a persistent object from the database.
     * @param sm The state manager of the object to be fetched.
     * @param fieldNumbers The numbers of the fields to be fetched.
     * @throws JPOXObjectNotFoundException if the object doesnt exist
     * @throws JPOXDataStoreException when an error occurs in the datastore communication
     */
    public void fetchObject(StateManager sm, int fieldNumbers[])
    {
        AbstractMemberMetaData[] fmds = null;
        if (fieldNumbers != null && fieldNumbers.length > 0)
        {
            // Convert the field numbers for this class into their metadata for the table
            fmds = new AbstractMemberMetaData[fieldNumbers.length];
            for (int i=0;i<fmds.length;i++)
            {
                fmds[i] = sm.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
            }

            if (sm.getPcObjectType() != StateManager.PC)
            {
                StringBuffer str = new StringBuffer();
                for (int i=0;i<fmds.length;i++)
                {
                    if (i > 0)
                    {
                        str.append(',');
                    }
                    str.append(fmds[i].getName());
                }
                JPOXLogger.PERSISTENCE.info("Request to load fields \"" + str.toString() +
                    "\" of class " + sm.getClassMetaData().getFullClassName() + " but object is embedded, so ignored");
            }
            else
            {
                if (storeMgr.getRuntimeManager() != null)
                {
                    storeMgr.getRuntimeManager().incrementFetchCount();
                }

                ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
                DatastoreClass table = storeMgr.getDatastoreClass(sm.getObject().getClass().getName(), clr);
                FetchRequest req = getFetchRequest(table, fmds, sm.getObject().getClass(), clr);
                req.execute(sm);
            }
        }
    }

    /**
     * Returns a request object that will fetch a row from the given table.
     * The store manager will cache the request object for re-use by subsequent requests to the same table.
     * @param table The table from which to fetch.
     * @param fieldMetaData MetaData for the fields corresponding to the columns to be fetched.
     * @param cls class of the object of the request
     * @param clr ClassLoader resolver
     * @return A fetch request object.
     */
    private FetchRequest getFetchRequest(DatastoreClass table, AbstractMemberMetaData[] fieldMetaData, Class cls,
            ClassLoaderResolver clr)
    {
        RequestIdentifier reqID = new RequestIdentifier(table, fieldMetaData, RequestIdentifier.Type.FETCH,
            cls.getName());
        FetchRequest req = (FetchRequest) requestsByID.get(reqID);
        if (req == null)
        {
            req = new FetchRequest(table, fieldMetaData, cls, clr);
            requestsByID.put(reqID, req);
        }
        return req;
    }

    // ------------------------------ Update ----------------------------------

    /**
     * Updates a persistent object in the database.
     * @param sm The state manager of the object to be updated.
     * @param fieldNumbers The numbers of the fields to be updated.
     * @throws JPOXDataStoreException when an error occurs in the datastore communication
     */
    public void updateObject(StateManager sm, int fieldNumbers[])
    {
        PersistenceConfiguration conf = storeMgr.getOMFContext().getPersistenceConfiguration();
        if (conf.getBooleanProperty("org.jpox.readOnlyDatastore"))
        {
            if (conf.getStringProperty("org.jpox.readOnlyDatastoreAction").equalsIgnoreCase("EXCEPTION"))
            {
                throw new DatastorePermissionException(LOCALISER.msg("032006",
                    StringUtils.toJVMIDString(sm.getObject())));
            }
            else
            {
                if (JPOXLogger.PERSISTENCE.isDebugEnabled())
                {
                    JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("032007",
                        StringUtils.toJVMIDString(sm.getObject())));
                }
                return;
            }
        }

        AbstractMemberMetaData[] fmds = null;
        if (fieldNumbers != null && fieldNumbers.length > 0)
        {
            // Convert the field numbers for this class into their metadata for the table
            fmds = new AbstractMemberMetaData[fieldNumbers.length];
            for (int i=0;i<fmds.length;i++)
            {
                fmds[i] = sm.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
            }

            if (storeMgr.getRuntimeManager() != null)
            {
                storeMgr.getRuntimeManager().incrementUpdateCount();
            }

            ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
            DatastoreClass dc = storeMgr.getDatastoreClass(sm.getObject().getClass().getName(), clr);
            updateTable(dc, sm, clr, fmds);
        }
    }

    /**
     * Convenience method to handle the update into the various tables that this object is persisted into.
     * @param table The table to process
     * @param sm StateManager for the object being updated
     * @param clr ClassLoader resolver
     * @param fieldMetaData MetaData for the fields being updated
     */
    private void updateTable(DatastoreClass table, StateManager sm, ClassLoaderResolver clr,
            AbstractMemberMetaData[] fieldMetaData)
    {
        DatastoreClass supertable = table.getSuperDatastoreClass();
        if (supertable != null)
        {
            // Process the superclass table first
            updateTable(supertable, sm, clr, fieldMetaData);
        }

        // Do the actual update of this table
        // TODO Assert if this table is not yet initialised ?
        UpdateRequest req = getUpdateRequest(table, fieldMetaData, sm.getObject().getClass(), clr);
        req.execute(sm);

        // Update any secondary tables
        Collection secondaryTables = table.getSecondaryDatastoreClasses();
        if (secondaryTables != null)
        {
            Iterator tablesIter = secondaryTables.iterator();
            while (tablesIter.hasNext())
            {
                // Process the secondary table
                SecondaryTable secTable = (SecondaryTable)tablesIter.next();
                updateTable(secTable, sm, clr, fieldMetaData);
            }
        }
    }

    /**
     * Returns a request object that will update a row in the given table.
     * The store manager will cache the request object for re-use by subsequent requests to the same table.
     * @param table The table in which to update.
     * @param fieldMetaData The metadata corresponding to the columns to be updated.
     *     MetaData whose columns exist in supertables will be ignored.
     * @param cls class of the object of the request
     * @param clr ClassLoader resolver
     * @return An update request object.
     */
    private UpdateRequest getUpdateRequest(DatastoreClass table, AbstractMemberMetaData[] fieldMetaData, Class cls,
            ClassLoaderResolver clr)
    {
        RequestIdentifier reqID = new RequestIdentifier(table, fieldMetaData, RequestIdentifier.Type.UPDATE,
            cls.getName());
        UpdateRequest req = (UpdateRequest) requestsByID.get(reqID);
        if (req == null)
        {
            req = new UpdateRequest(table, fieldMetaData, cls, clr);
            requestsByID.put(reqID, req);
        }
        return req;
    }

    // ------------------------------ Delete ----------------------------------

    /**
     * Deletes a persistent object from the database.
     * @param sm The state manager of the object to be deleted.
     * @throws JPOXDataStoreException when an error occurs in the datastore communication
     */
    public void deleteObject(StateManager sm)
    {
        PersistenceConfiguration conf = storeMgr.getOMFContext().getPersistenceConfiguration();
        if (conf.getBooleanProperty("org.jpox.readOnlyDatastore"))
        {
            if (conf.getStringProperty("org.jpox.readOnlyDatastoreAction").equalsIgnoreCase("EXCEPTION"))
            {
                throw new DatastorePermissionException(LOCALISER.msg("032008",
                    StringUtils.toJVMIDString(sm.getObject())));
            }
            else
            {
                if (JPOXLogger.PERSISTENCE.isDebugEnabled())
                {
                    JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("032009",
                        StringUtils.toJVMIDString(sm.getObject())));
                }
                return;
            }
        }

        if (storeMgr.getRuntimeManager() != null)
        {
            storeMgr.getRuntimeManager().incrementDeleteCount();
        }

        ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
        DatastoreClass dc = storeMgr.getDatastoreClass(sm.getObject().getClass().getName(), clr);
        deleteTable(dc, sm, clr);
    }

    /**
     * Convenience method to handle the delete from the various tables that this object is persisted into.
     * @param table The table to process
     * @param sm StateManager for the object being deleted
     * @param clr ClassLoader resolver
     */
    private void deleteTable(DatastoreClass table, StateManager sm, ClassLoaderResolver clr)
    {
        // Delete any secondary tables
        Collection secondaryTables = table.getSecondaryDatastoreClasses();
        if (secondaryTables != null)
        {
            Iterator tablesIter = secondaryTables.iterator();
            while (tablesIter.hasNext())
            {
                // Process the secondary table
                SecondaryTable secTable = (SecondaryTable)tablesIter.next();
                deleteTable(secTable, sm, clr);
            }
        }

        // Do the actual delete of this table
        // TODO Assert if this table is not yet initialised ?
        DeleteRequest req = getDeleteRequest(table, sm.getObject().getClass(), clr);
        req.execute(sm);

        DatastoreClass supertable = table.getSuperDatastoreClass();
        if (supertable != null)
        {
            // Process the superclass table last
            deleteTable(supertable, sm, clr);
        }
    }

    /**
     * Returns a request object that will delete a row from the given table.
     * The store manager will cache the request object for re-use by subsequent requests to the same table.
     * @param table The table from which to delete.
     * @param cls class of the object of the request
     * @param clr ClassLoader resolver
     * @return A deletion request object.
     */
    private DeleteRequest getDeleteRequest(DatastoreClass table, Class cls, ClassLoaderResolver clr)
    {
        RequestIdentifier reqID = new RequestIdentifier(table, null, RequestIdentifier.Type.DELETE, cls.getName());
        DeleteRequest req = (DeleteRequest) requestsByID.get(reqID);
        if (req == null)
        {
            req = new DeleteRequest(table, cls, clr);
            requestsByID.put(reqID, req);
        }
        return req;
    }

    // ------------------------------ Locate ----------------------------------

    /**
     * Locates this object in the datastore.
     * @param sm The StateManager for the object to be found
     * @throws JPOXObjectNotFoundException if the object doesnt exist
     * @throws JPOXDataStoreException when an error occurs in the datastore communication
     */
    public void locateObject(StateManager sm)
    {
        ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
        DatastoreClass table = storeMgr.getDatastoreClass(sm.getObject().getClass().getName(), clr);
        LocateRequest req = getLocateRequest(table, sm.getObject().getClass().getName());
        req.execute(sm);
    }

    /**
     * Returns a request object that will locate a row from the given table.
     * The store manager will cache the request object for re-use by subsequent requests to the same table.
     * @param table The table from which to locate.
     * @param className the class name of the object of the request
     * @return A locate request object.
     */
    private LocateRequest getLocateRequest(DatastoreClass table, String className)
    {
        RequestIdentifier reqID = new RequestIdentifier(table, null, RequestIdentifier.Type.LOCATE, className);
        LocateRequest req = (LocateRequest) requestsByID.get(reqID);
        if (req == null)
        {
            req = new LocateRequest(table);
            requestsByID.put(reqID, req);
        }
        return req;
    }

    // ------------------------------ Find ----------------------------------

    /**
     * Method to return a persistable object with the specified id. Optional operation for StoreManagers.
     * Should return a (at least) hollow PersistenceCapable object if the store manager supports the operation.
     * If the StoreManager is managing the in-memory object instantiation (as part of co-managing the object
     * lifecycle in general), then the StoreManager has to create the object during this call (if it is not
     * already created). Most relational databases leave the in-memory object instantion to JPOX Core, but some
     * object databases may manage the in-memory object instantion, effectively preventing JPOX Core of doing this.
     * <p>
     * StoreManager implementations may simply return null, indicating that they leave the object instantiate to
     * JPOX. Other implementations may instantiate the object in question (whether the implementation may trust
     * that the object is not already instantiated has still to be determined). If an implementation believes
     * that an object with the given ID should exist, but in fact does not exist, then the implementation should
     * throw a RuntimeException. It should not silently return null in this case.
     * </p>
     * @param om the ObjectManager which will manage the object
     * @param id the id of the object in question.
     * @return a persistable object with a valid object state (for example: hollow) or null,
     *     indicating that the implementation leaves the instantiation work to JPOX.
     */
    public Object findObject(ObjectManager om, Object id)
    {
        return null;
    }

    // ------------------------------ Convenience ----------------------------------

    /**
     * Convenience method to remove all requests since the schema has changed.
     */
    public void removeAllRequests()
    {
        synchronized (requestsByID)
        {
            requestsByID.clear();
        }
    }

    /**
     * Convenience method to remove all requests that use a particular table since the structure
     * of the table has changed potentially leading to missing columns in the cached version.
     * @param table The table
     */
    public void removeRequestsForTable(DatastoreClass table)
    {
        synchronized(requestsByID)
        {
            // Synchronise on the "requestsById" set since while it is "synchronised itself, all iterators needs this sync
            Set keySet = new HashSet(requestsByID.keySet());
            Iterator keyIter = keySet.iterator();
            while (keyIter.hasNext())
            {
                RequestIdentifier reqId = (RequestIdentifier)keyIter.next();
                if (reqId.getTable() == table)
                {
                    requestsByID.remove(reqId);
                }
            }
        }
    }
}
TOP

Related Classes of org.jpox.store.rdbms.RDBMSPersistenceHandler

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.