Package org.jpox.store.rdbms

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

/**********************************************************************
Copyright (c) 2007 Erik Bengtson 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.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

import org.jpox.ClassLoaderResolver;
import org.jpox.ConnectionFactory;
import org.jpox.ManagedConnection;
import org.jpox.ManagedConnectionResourceListener;
import org.jpox.OMFContext;
import org.jpox.ObjectManager;
import org.jpox.PersistenceConfiguration;
import org.jpox.UserTransaction;
import org.jpox.exceptions.ClassNotResolvedException;
import org.jpox.exceptions.ConnectionFactoryNotFoundException;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.exceptions.UnsupportedConnectionFactoryException;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.rdbms.adapter.RDBMSAdapter;
import org.jpox.store.rdbms.datasource.JPOXDataSourceFactory;
import org.jpox.transaction.TransactionUtils;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.StringUtils;

/**
* ConnectionFactory for RDBMS datastores.
* Each instance is a factory of Transactional connection or NonTransactional connection.
*/
public class ConnectionFactoryImpl implements ConnectionFactory
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER_RDBMS = Localiser.getInstance("org.jpox.store.rdbms.Localisation",
        RDBMSManager.class.getClassLoader());

    /** datasources. */
    Object[] dataSource;
   
    /** The underlying ObjectManagerFactory context. */
    OMFContext omfContext;

    Map options = new HashMap();
   
    /**
     * Constructor.
     * @param omfContext The OMF context
     * @param resourceType either tx or nontx
     */
    public ConnectionFactoryImpl(OMFContext omfContext, String resourceType)
    {
        this.omfContext = omfContext;
        PersistenceConfiguration config = omfContext.getPersistenceConfiguration();

        if (resourceType.equals("tx"))
        {
            initDataSourceTx(config);
        }
        else
        {
            initDataSourceNonTx(config);
        }
    }

    private void initDataSourceTx(PersistenceConfiguration config)
    {
        options.put("resource-type", this.omfContext.getPersistenceConfiguration().getStringProperty(
            "org.jpox.connection.resourceType"));

        // Set the transactional DataSource using connection factory (1)
        if (config.hasProperty("org.jpox.ConnectionFactory"))
        {
            Object cf = config.getProperty("org.jpox.ConnectionFactory");
            if (!(cf instanceof DataSource) && !(cf instanceof XADataSource))
            {
                throw new UnsupportedConnectionFactoryException(cf);
            }
            dataSource = new DataSource[1];
            dataSource[0] = cf;
        }
        else if (config.hasProperty("org.jpox.ConnectionFactoryName"))
        {
            String[] connectionFactoryNames = StringUtils.split(config.getStringProperty("org.jpox.ConnectionFactoryName"),",");
            dataSource = new DataSource[connectionFactoryNames.length];
            for (int i=0; i<connectionFactoryNames.length; i++)
            {
                dataSource[i] = lookupDataSource(connectionFactoryNames[i]);
            }
        }
        else
        {
            dataSource = new DataSource[1];
            String poolingType = getPoolingType(config);

            // User has requested internal database connection pooling so check the registered plugins
            try
            {
                // Create the DataSource to be used
                JPOXDataSourceFactory dataSourceFactory =
                    (JPOXDataSourceFactory)omfContext.getPluginManager().createExecutableExtension(
                        "org.jpox.datasource", "name", poolingType, "class-name", null, null);
                if (dataSourceFactory == null)
                {
                    // User has specified a pool plugin that has not registered
                    throw new JPOXUserException(LOCALISER_RDBMS.msg("047003",
                        poolingType)).setFatal();
                }

                // Create the JPOXDataSourceFactory
                dataSource[0] = dataSourceFactory.makePooledDataSource(omfContext);
                if (JPOXLogger.CONNECTION.isDebugEnabled())
                {
                    JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("047008", "transactional", poolingType));
                }
            }
            catch (ClassNotFoundException cnfe)
            {
                throw new JPOXUserException(LOCALISER_RDBMS.msg("047003", poolingType),
                    cnfe).setFatal();
            }
            catch (Exception e)
            {
                if (e instanceof InvocationTargetException)
                {
                    InvocationTargetException ite = (InvocationTargetException)e;
                    throw new JPOXException(LOCALISER_RDBMS.msg("047004", poolingType,
                        ite.getTargetException().getMessage()), ite.getTargetException()).setFatal();
                }
                else
                {
                    throw new JPOXException(LOCALISER_RDBMS.msg("047004", poolingType,
                        e.getMessage()),e).setFatal();
                }
            }
        }
    }
   
    private void initDataSourceNonTx(PersistenceConfiguration config)
    {
        options.put("resource-type", this.omfContext.getPersistenceConfiguration().getStringProperty(
            "org.jpox.connection2.resourceType"));

        // Set the transactional DataSource using connection factory (1)
        if (config.hasProperty("org.jpox.ConnectionFactory2"))
        {
            Object cf2 = config.getProperty("org.jpox.ConnectionFactory2");
            if (!(cf2 instanceof DataSource) && !(cf2 instanceof XADataSource))
            {
                throw new UnsupportedConnectionFactoryException(cf2);
            }
            dataSource = new DataSource[1];
            dataSource[0] = cf2;
        }
        else if (config.hasProperty("org.jpox.ConnectionFactory2Name"))
        {
            String[] connectionFactoryNames = StringUtils.split(config.getStringProperty("org.jpox.ConnectionFactory2Name"),",");
            dataSource = new DataSource[connectionFactoryNames.length];
            for (int i=0; i<connectionFactoryNames.length; i++)
            {
                dataSource[i] = lookupDataSource(connectionFactoryNames[i]);
            }
        }
        else if (config.hasProperty("org.jpox.ConnectionURL"))
        {
            dataSource = new DataSource[1];
            String poolingType = getPoolingType(config);

            // User has requested internal database connection pooling so check the registered plugins
            try
            {
                // Create the DataSource to be used
                JPOXDataSourceFactory dataSourceFactory =
                    (JPOXDataSourceFactory)omfContext.getPluginManager().createExecutableExtension(
                        "org.jpox.datasource", "name", poolingType, "class-name", null, null);
                if (dataSourceFactory == null)
                {
                    // User has specified a pool plugin that has not registered
                    throw new JPOXUserException(LOCALISER_RDBMS.msg("047003",
                        poolingType)).setFatal();
                }

                // Create the JPOXDataSourceFactory
                dataSource[0] = dataSourceFactory.makePooledDataSource(omfContext);
                if (JPOXLogger.CONNECTION.isDebugEnabled())
                {
                    JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("047008", "nontransactional", poolingType));
                }
            }
            catch (ClassNotFoundException cnfe)
            {
                throw new JPOXUserException(LOCALISER_RDBMS.msg("047003", poolingType),
                    cnfe).setFatal();
            }
            catch (Exception e)
            {
                if (e instanceof InvocationTargetException)
                {
                    InvocationTargetException ite = (InvocationTargetException)e;
                    throw new JPOXException(LOCALISER_RDBMS.msg("047004", poolingType,
                        ite.getTargetException().getMessage()), ite.getTargetException()).setFatal();
                }
                else
                {
                    throw new JPOXException(LOCALISER_RDBMS.msg("047004", poolingType,
                        e.getMessage()),e).setFatal();
                }
            }
        }
        else
        {
            //defaults to the transactional datasource
            initDataSourceTx(config);
        }       
    }

    /**
     * Accessor for the pooling type (if any).
     * If set to a value will use that, otherwise (if unset) will check if any supported
     * pooling capabilities are present and available. Currently checks DBCP, C3P0, Proxool.
     * @param config Persistence configuration
     * @return Pooling type to use
     */
    private String getPoolingType(PersistenceConfiguration config)
    {
        String poolingType = config.getStringProperty("org.jpox.connectionPoolingType");
        ClassLoaderResolver clr = omfContext.getClassLoaderResolver(null);

        if (poolingType == null)
        {
            // Check if DBCP available
            try
            {
                // Need jpox-dbcp, commons-dbcp, commons-pool, commons-collections
                clr.classForName("org.jpox.store.rdbms.datasource.dbcp.DBCPDataSourceFactory");
                clr.classForName("org.apache.commons.pool.ObjectPool");
                clr.classForName("org.apache.commons.dbcp.ConnectionFactory");
                poolingType = "DBCP";
            }
            catch (ClassNotResolvedException cnre)
            {
                // DBCP not available
            }
        }
        if (poolingType == null)
        {
            // Check if C3P0 is available
            try
            {
                // Need jpox-c3p0, c3p0
                clr.classForName("org.jpox.store.rdbms.datasource.c3p0.C3P0DataSourceFactory");
                clr.classForName("com.mchange.v2.c3p0.ComboPooledDataSource");
                poolingType = "C3P0";
            }
            catch (ClassNotResolvedException cnre)
            {
                // C3P0 not available
            }
        }
        if (poolingType == null)
        {
            // Check if Proxool is available
            try
            {
                // Need jpox-proxool, proxool, commons-logging
                clr.classForName("org.jpox.store.rdbms.datasource.proxool.ProxoolDataSourceFactory");
                clr.classForName("org.logicalcobwebs.proxool.ProxoolDriver");
                clr.classForName("org.apache.commons.logging.Log");
                poolingType = "Proxool";
            }
            catch (ClassNotResolvedException cnre)
            {
                // Proxool not available
            }
        }

        if (poolingType == null || poolingType.equalsIgnoreCase("None"))
        {
            // Fallback to none ("default")
            poolingType = "default";
        }
        return poolingType;
    }

    /**
     * Method to return a connection (either already existing from cache, or newly allocated) enlisting
     * it in any transaction.
     * @param om The ObjectManager (or null)
     * @param options Options for when creating the connection
     * @return The ManagedConnection
     */
    public ManagedConnection getConnection(ObjectManager om, Map options)
    {
        Map addedOptions = new HashMap();
        if (options != null)
        {
            addedOptions.putAll(options);
        }
        addedOptions.putAll(this.options);

        final ManagedConnectionImpl connection =
            (ManagedConnectionImpl) omfContext.getConnectionManager().allocateConnection(this, om, addedOptions);
        connection.incrementUseCount();
        return connection;
    }

    /**
     * Method to create a new ManagedConnection.
     * @param om ObjectManager (if any)
     * @param transactionOptions Transaction options
     * @return The ManagedConnection
     */
    public ManagedConnection createManagedConnection(ObjectManager om, Map transactionOptions)
    {
        return new ManagedConnectionImpl(transactionOptions);
    }

    class ManagedConnectionImpl implements ManagedConnection
    {
        Object conn;

        /** if managed connection is managed by TransactionManager, then can reuse several times inside same txn. */
        boolean managed = false;

        boolean locked = false;
        int isolation;
        List listeners = new ArrayList();

        /** Count on the number of outstanding uses of this connection. Incremented on get. Decremented on close. */
        int useCount = 0;

        ManagedConnectionImpl(Map transactionOptions)
        {
            if (transactionOptions != null && transactionOptions.get("transaction.isolation") != null)
            {
                isolation = ((Number) transactionOptions.get("transaction.isolation")).intValue();
            }
            else
            {
                isolation = TransactionUtils.getTransactionIsolationLevelForName(
                    omfContext.getPersistenceConfiguration().getStringProperty("org.jpox.transactionIsolation"));
            }
        }

        void incrementUseCount()
        {
            useCount = useCount + 1;
        }

        /**
         * Release this connection.
         * Releasing this connection will allow this managed connection to be used one or more times
         * inside the same transaction. If this managed connection is managed by a transaction manager,
         * release is a no-op, otherwise the physical connection is closed
         */
        public void release()
        {
            if (!managed)
            {
                useCount = useCount -1;
                if (useCount == 0)
                {
                    // Close if this is the last use of the connection
                    close();
                }
            }
        }

        /**
         * Obtain a XAResource which can be enlisted in a transaction
         */
        public XAResource getXAResource()
        {
            if (getConnection() instanceof Connection)
            {
                return new EmulatedXAResource((Connection)getConnection());
            }
            else
            {
                try
                {
                    return ((XAConnection)getConnection()).getXAResource();
                }
                catch (SQLException e)
                {
                    throw new JPOXDataStoreException(e.getMessage(),e);
                }
            }                       
        }

        /**
         * Create a connection to the resource
         */
        public Object getConnection()
        {
            if (this.conn == null)
            {
                Connection cnx = null;
                try
                {
                    MappedStoreManager storeMgr = (MappedStoreManager)omfContext.getStoreManager();
                    if (storeMgr != null && storeMgr.getDatastoreAdapter() != null)
                    {
                        // Create Connection following DatastoreAdapter capabilities
                        RDBMSAdapter rdba = (RDBMSAdapter)storeMgr.getDatastoreAdapter();
                        int reqdIsolationLevel = isolation;
                        if (rdba.getRequiredTransactionIsolationLevel() >= 0)
                        {
                            // Override with the adapters required isolation level
                            reqdIsolationLevel = rdba.getRequiredTransactionIsolationLevel();
                        }

                        DataSource[] ds = (DataSource[])dataSource;
                        cnx = new ConnectionProviderPriorityList().getConnection(ds);
                        boolean succeeded = false;
                        try
                        {
                            if (reqdIsolationLevel == UserTransaction.TRANSACTION_NONE)
                            {
                                if (!cnx.getAutoCommit())
                                {
                                    cnx.setAutoCommit(true);
                                }
                            }
                            else
                            {
                                if (cnx.getAutoCommit())
                                {
                                    cnx.setAutoCommit(false);
                                }
                                if (rdba.supportsTransactionIsolationLevel(reqdIsolationLevel))
                                {
                                    int currentIsolationLevel = cnx.getTransactionIsolation();
                                    if (currentIsolationLevel != reqdIsolationLevel)
                                    {
                                        cnx.setTransactionIsolation(reqdIsolationLevel);
                                    }
                                }
                                else
                                {
                                    JPOXLogger.DATASTORE.warn(LOCALISER_RDBMS.msg(
                                        "051008",
                                        reqdIsolationLevel));
                                }
                            }

                            succeeded = true;
                            if (JPOXLogger.CONNECTION.isDebugEnabled())
                            {
                                JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("052002",
                                    cnx.toString(),
                                    TransactionUtils.getNameForTransactionIsolationLevel(reqdIsolationLevel)));
                            }

                            if (reqdIsolationLevel != isolation && isolation == UserTransaction.TRANSACTION_NONE)
                            {
                                // User asked for a level that implies auto-commit so make sure it has that
                                if (!cnx.getAutoCommit())
                                {
                                    cnx.setAutoCommit(true);
                                }
                            }
                        }
                        catch (SQLException e)
                        {
                            throw new JPOXDataStoreException(e.getMessage(),e);
                        }                       
                        finally
                        {
                            if (!succeeded && !cnx.isClosed())
                            {
                                String cnxStr = cnx.toString();
                                cnx.close();
                                if (JPOXLogger.CONNECTION.isDebugEnabled())
                                {
                                    JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("052003", cnxStr));
                                }
                            }
                        }
                    }
                    else
                    {
                        // Create basic Connection since no DatastoreAdapter created yet
                        cnx = ((DataSource)dataSource[0]).getConnection();
                        if (cnx == null)
                        {
                            String msg = LOCALISER_RDBMS.msg("052000", dataSource[0]);
                            JPOXLogger.CONNECTION.error(msg);
                            throw new JPOXDataStoreException(msg);
                        }
                        if (JPOXLogger.CONNECTION.isDebugEnabled())
                        {
                            JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("052001", cnx.toString()));
                        }
                    }
                }
                catch (SQLException e)
                {
                    throw new JPOXDataStoreException(e.getMessage(),e);
                }

                this.conn = cnx;
            }
            return this.conn;
        }

        /**
         * Close the connection
         */
        public void close()
        {
            for (int i=0; i<listeners.size(); i++)
            {
                ((ManagedConnectionResourceListener)listeners.get(i)).managedConnectionPreClose();
            }
            Connection conn = null;
            if (this.conn != null && this.conn instanceof Connection)
            {
                conn = (Connection) this.conn;
            }
            else if (this.conn != null && this.conn instanceof XAConnection)
            {
                try
                {
                    conn = ((XAConnection) this.conn).getConnection();
                }
                catch (SQLException e)
                {
                    throw new JPOXDataStoreException(e.getMessage(), e);
                }
            }
            if (conn != null)
            {
                try
                {
                    //if this connection is not enlisted in a TransactionManager (such as the internal TM, or an external JTA container)
                    //and autocommit is not enabled at connection, we commit before closing
                    //Usually needed by NonTransactionalRead and NonTransactionalWrite modes, or connections used to obtain Object IDs 
                    if (!managed)
                    {
                        if (!conn.isClosed() && !conn.getAutoCommit())
                        {
                            conn.commit();
                            if (JPOXLogger.CONNECTION.isDebugEnabled())
                            {
                                JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("052005", conn.toString()));
                            }
                        }
                    }
                    if (!conn.isClosed())
                    {
                        String connStr = conn.toString();
                        conn.close();
                        if (JPOXLogger.CONNECTION.isDebugEnabled())
                        {
                            JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("052003", connStr));
                        }
                    }
                    else
                    {
                        if (JPOXLogger.CONNECTION.isDebugEnabled())
                        {
                            JPOXLogger.CONNECTION.debug(LOCALISER_RDBMS.msg("052004", conn.toString()));
                        }
                    }
                }
                catch (SQLException e)
                {
                    throw new JPOXDataStoreException(e.getMessage(),e);
                }
            }
            try
            {
                for( int i=0; i<listeners.size(); i++ )
                {
                    ((ManagedConnectionResourceListener)listeners.get(i)).managedConnectionPostClose();
                }
            }
            finally
            {
                listeners.clear();
            }
        }

        /**
         * Whether this connection is managed by a transaction manager
         */
        public void setManagedResource()
        {
            this.managed = true;           
        }
       
        public boolean isLocked()
        {
            return locked;
        }
       
        public void lock()
        {
            locked = true;
        }
       
        public void unlock()
        {
            locked = false;
        }

        public void addListener(ManagedConnectionResourceListener listener)
        {
            listeners.add(listener);           
        }

        public void removeListener(ManagedConnectionResourceListener listener)
        {
            listeners.remove(listener);           
        }

        public String toString()
        {
            //log this object, and connection associated
            return "[" + super.toString() + ", "+conn+"]";
        }
        public void flush()
        {
            for( int i=0; i<listeners.size(); i++ )
            {
                ((ManagedConnectionResourceListener)listeners.get(i)).managedConnectionFlushed();
            }
        }
    }
   
    /**
     * Emulate the two phase protocol for non XA
     */
    class EmulatedXAResource implements XAResource
    {
        Connection conn;
       
        EmulatedXAResource(Connection conn)
        {
            this.conn = conn;
        }
        public void commit(Xid xid, boolean flags) throws XAException
        {
            JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" is committing for transaction "+xid.toString()+" with flags "+flags);
           
            try
            {
                conn.commit();
                JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" committed connection for transaction "+xid.toString()+" with flags "+flags);
            }
            catch (SQLException e)
            {
                JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" failed to commit connection for transaction "+xid.toString()+" with flags "+flags);
                XAException xe = new XAException(StringUtils.getStringFromStackTrace(e));
                xe.initCause(e);
                throw xe;
            }
        }

        public void end(Xid xid, int flags) throws XAException
        {
            JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" is ending for transaction "+xid.toString()+" with flags "+flags);
            //ignore
        }

        public void forget(Xid arg0) throws XAException
        {
            //ignore
        }

        public int getTransactionTimeout() throws XAException
        {
            return 0;
        }

        public boolean isSameRM(XAResource xares) throws XAException
        {
            return (this == xares);
        }

        public int prepare(Xid xid) throws XAException
        {
            JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" is preparing for transaction "+xid.toString());
           
            return 0;
        }

        public Xid[] recover(int flags) throws XAException
        {
            throw new XAException("Unsupported operation");
        }

        public void rollback(Xid xid) throws XAException
        {
            JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" is rolling back for transaction "+xid.toString());
            try
            {
                conn.rollback();
                JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" rolled back connection for transaction "+xid.toString());
            }
            catch (SQLException e)
            {
                JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" failed to rollback connection for transaction "+xid.toString());
                XAException xe = new XAException(StringUtils.getStringFromStackTrace(e));
                xe.initCause(e);
                throw xe;
            }
        }

        public boolean setTransactionTimeout(int arg0) throws XAException
        {
            return false;
        }

        public void start(Xid xid, int flags) throws XAException
        {
            JPOXLogger.CONNECTION.debug("Managed connection "+this.toString()+" is starting for transaction "+xid.toString()+" with flags "+flags);
            //ignore
        }       
    }
   
    /**
     * Looks up a DataSource object in JNDI. This only permits lookup for DataSources locally
     * For remote DataSources usage, configure the PersistenceConfiguration as an object (ConnectionFactory), instead of
     * name (ConnectionFactoryName)
     * @param name The JNDI name of the DataSource.
     * @return  The DataSource object.
     * @exception ConnectionFactoryNotFoundException If a JNDI lookup failure occurs.
     * @exception UnsupportedConnectionFactoryException If the object is not a javax.sql.DataSource.
     */
    private Object lookupDataSource(String name)
    {
        Object obj;

        try
        {
            obj = new InitialContext().lookup(name);
        }
        catch (NamingException e)
        {
            throw new ConnectionFactoryNotFoundException(name, e);
        }

        if (!(obj instanceof DataSource) && !(obj instanceof XADataSource))
        {
            throw new UnsupportedConnectionFactoryException(obj);
        }

        return obj;
    }
}
TOP

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

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.