001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/RolapConnectionPool.java#9 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2003-2006 Robin Bagot, Julian Hyde and others
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.rolap;
011    
012    import mondrian.olap.Util;
013    import org.apache.commons.dbcp.*;
014    import org.apache.commons.pool.ObjectPool;
015    import org.apache.commons.pool.impl.GenericObjectPool;
016    
017    import javax.sql.DataSource;
018    import java.util.Map;
019    import java.util.HashMap;
020    
021    /**
022     * Singleton class that holds a connection pool.
023     * Call RolapConnectionPool.instance().getPoolingDataSource(connectionFactory)
024     * to get a DataSource in return that is a pooled data source.
025     */
026    class RolapConnectionPool {
027    
028        public static RolapConnectionPool instance() {
029            return instance;
030        }
031        private static final RolapConnectionPool instance = new RolapConnectionPool();
032    
033        private final Map<Object, ObjectPool> mapConnectKeyToPool =
034            new HashMap<Object, ObjectPool>();
035    
036        private RolapConnectionPool() {
037        }
038    
039    
040        /**
041         * Sets up a pooling data source for connection pooling.
042         * This can be used if the application server does not have a pooling
043         * dataSource already configured.
044         * This takes a normal jdbc connection string, and requires a jdbc
045         * driver to be loaded, and then uses a
046         * {@link DriverManagerConnectionFactory} to create connections to the
047         * database.
048         * An alternative method of configuring a pooling driver is to use an external
049         * configuration file. See the the Apache jakarta-commons commons-pool
050         * documentation.
051         *
052         * @param key Identifies which connection factory to use. A typical key is
053         *   a JDBC connect string, since each JDBC connect string requires a
054         *   different connection factory.
055         * @param connectionFactory Creates connections from an underlying
056         *   JDBC connect string or DataSource
057         * @return a pooling DataSource object
058         */
059        public synchronized DataSource getPoolingDataSource(Object key,
060                                           ConnectionFactory connectionFactory) {
061            ObjectPool connectionPool = getPool(key, connectionFactory);
062            // create pooling datasource
063            return new PoolingDataSource(connectionPool);
064        }
065    
066        /**
067         * Clears the connection pool for testing purposes
068         */
069        void clearPool() {
070            mapConnectKeyToPool.clear();
071        }
072    
073        /**
074         * Gets or creates a connection pool for a particular connect
075         * specification.
076         */
077        private synchronized ObjectPool getPool(
078            Object key,
079            ConnectionFactory connectionFactory)
080        {
081            ObjectPool connectionPool = mapConnectKeyToPool.get(key);
082            if (connectionPool == null) {
083                // use GenericObjectPool, which provides for resource limits
084                connectionPool = new GenericObjectPool(
085                    null, // PoolableObjectFactory, can be null
086                    50, // max active
087                    GenericObjectPool.WHEN_EXHAUSTED_BLOCK, // action when exhausted
088                    3000, // max wait (milli seconds)
089                    10, // max idle
090                    false, // test on borrow
091                    false, // test on return
092                    60000, // time between eviction runs (millis)
093                    5, // number to test on eviction run
094                    30000, // min evictable idle time (millis)
095                    true); // test while idle
096    
097                // create a PoolableConnectionFactory
098                AbandonedConfig abandonedConfig = new AbandonedConfig();
099                // flag to remove abandoned connections from pool
100                abandonedConfig.setRemoveAbandoned(true);
101                // timeout (seconds) before removing abandoned connections
102                abandonedConfig.setRemoveAbandonedTimeout(300);
103                // Flag to log stack traces for application code which abandoned a
104                // Statement or Connection
105                abandonedConfig.setLogAbandoned(true);
106                PoolableConnectionFactory poolableConnectionFactory
107                    = new PoolableConnectionFactory(
108                        // the connection factory
109                        connectionFactory,
110                        // the object pool
111                        connectionPool,
112                        // statement pool factory for pooling prepared statements,
113                        // or null for no pooling
114                        null,
115                        // validation query (must return at least 1 row e.g. Oracle:
116                        // select count(*) from dual) to test connection, can be
117                        // null
118                        null,
119                        // default "read only" setting for borrowed connections
120                        false,
121                        // default "auto commit" setting for returned connections
122                        true,
123                        // AbandonedConfig object configures how to handle abandoned
124                        // connections
125                        abandonedConfig);
126    
127                // "poolableConnectionFactory" has registered itself with
128                // "connectionPool", somehow, so we don't need the value any more.
129                Util.discard(poolableConnectionFactory);
130                mapConnectKeyToPool.put(key, connectionPool);
131            }
132            return connectionPool;
133        }
134    
135    }
136    
137    // End RolapConnectionPool.java