package ca.sqlpower.sql; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import org.apache.commons.dbcp.ConnectionFactory; import org.apache.commons.dbcp.DriverManagerConnectionFactory; import org.apache.commons.dbcp.PoolableConnectionFactory; import org.apache.commons.dbcp.PoolingDataSource; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.impl.GenericObjectPool; /** * Connection pool wrapper class, providing a simple connection pool interface * to SQL Power applications without exposing the underlying pool's interface. * * In this case, it is implemented using Apache Commons DBCP, plus some * customized classes such as the ca.sqlpower.sql.PoolableStatementClosingConnection * and the ca.sqlpower.sql.StatementClosingPoolableConnectionFactory. * * @author Dan Fraser * @version $Id$ */ public class Pool { private static Map pools = new HashMap(); private static Map objPools = new HashMap(); // for debugging and stats tracking private DBConnectionSpec dbcs; private String poolName; private PoolingDataSource dataSource; ObjectPool connectionPool; /** * Prepares a connection pool for the specified DBConnectionSpec. Adds * the pool to a cache of pools for re-use whenever the DBConnectionSpec, * username, and password are re-used. This essentially creates one pool * per unique database/user combination. */ public Pool(DBConnectionSpec dbcs) throws Exception { this.dbcs = dbcs; poolName = dbcs.getUrl()+"-"+dbcs.getUser()+"-"+dbcs.getPass(); dataSource = (PoolingDataSource) pools.get(poolName); connectionPool = (ObjectPool) objPools.get(poolName); // for debugging if (dataSource == null) { GenericObjectPool.Config poolConfig = new GenericObjectPool.Config(); // XXX: this should come from a properties file. poolConfig.maxActive = 100; poolConfig.maxIdle = 50; poolConfig.maxWait = 10000; poolConfig.minEvictableIdleTimeMillis = 1000*60*5; poolConfig.timeBetweenEvictionRunsMillis = 10000; poolConfig.numTestsPerEvictionRun = 5; poolConfig.testOnBorrow = true; connectionPool = new GenericObjectPool(null,poolConfig); ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(dbcs.getUrl(),dbcs.getUser(),dbcs.getPass()); PoolableConnectionFactory poolableConnectionFactory = new StatementClosingPoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true); poolableConnectionFactory.setValidationQuery("select 1 from def_param"); this.dataSource = new PoolingDataSource(connectionPool); pools.put(poolName, dataSource); objPools.put(poolName, connectionPool); // for debugging } else { // found connection pool in cache } } /** * Returns a Connection from the pool. Be sure to close it when you're done * with it! * * <p>Note: if the getConnection() call throws a SQLException, * this method intercepts it and removes this connection pool from * the cache. This is important because we don't want to keep * pools of connections with invalid username/password * combinations! */ public Connection getConnection() throws SQLException { Connection con; try { con = dataSource.getConnection(); } catch (SQLException e) { pools.remove(poolName); objPools.remove(poolName); throw e; } return con; } }