001    /*
002    // $Id: //open/mondrian-release/3.0/src/main/mondrian/rolap/RolapConnectionPool.java#2 $
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    
098                // create a PoolableConnectionFactory
099                AbandonedConfig abandonedConfig = new AbandonedConfig();
100                // flag to remove abandoned connections from pool
101                abandonedConfig.setRemoveAbandoned(true);
102                // timeout (seconds) before removing abandoned connections
103                abandonedConfig.setRemoveAbandonedTimeout(300);
104                // Flag to log stack traces for application code which abandoned a
105                // Statement or Connection
106                abandonedConfig.setLogAbandoned(true);
107                PoolableConnectionFactory poolableConnectionFactory
108                    = new PoolableConnectionFactory(
109                        // the connection factory
110                        connectionFactory,
111                        // the object pool
112                        connectionPool,
113                        // statement pool factory for pooling prepared statements,
114                        // or null for no pooling
115                        null,
116                        // validation query (must return at least 1 row e.g. Oracle:
117                        // select count(*) from dual) to test connection, can be
118                        // null
119                        null,
120                        // default "read only" setting for borrowed connections
121                        false,
122                        // default "auto commit" setting for returned connections
123                        true,
124                        // AbandonedConfig object configures how to handle abandoned
125                        // connections
126                        abandonedConfig
127                );
128                // "poolableConnectionFactory" has registered itself with
129                // "connectionPool", somehow, so we don't need the value any more.
130                Util.discard(poolableConnectionFactory);
131                mapConnectKeyToPool.put(key, connectionPool);
132            }
133            return connectionPool;
134        }
135    
136    }
137    
138    // End RolapConnectionPool.java