/* * Aipo is a groupware program developed by TOWN, Inc. * Copyright (C) 2004-2015 TOWN, Inc. * http://www.aipo.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.apache.cayenne.conf; import java.sql.Connection; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import org.apache.cayenne.ConfigurationException; import org.apache.commons.dbcp.ConnectionFactory; import org.apache.commons.dbcp.DriverManagerConnectionFactory; import org.apache.commons.dbcp.PoolableConnectionFactory; import org.apache.commons.dbcp.ThreadPoolingDataSource; import org.apache.commons.pool.KeyedObjectPoolFactory; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory; import org.apache.commons.pool.impl.GenericObjectPool; /** * @see DBCPDataSourceBuilder */ class CustomDBCPDataSourceBuilder { // DataSource Properties static final String DRIVER_CLASS_NAME = "driverClassName"; static final String URL = "url"; static final String USER_NAME = "username"; static final String PASSWORD = "password"; // Connection Pool Properties static final String MAX_ACTIVE = "maxActive"; static final String MIN_IDLE = "minIdle"; static final String MAX_IDLE = "maxIdle"; static final String MAX_WAIT = "maxWait"; static final String VALIDATION_QUERY = "validationQuery"; static final String TEST_ON_BORROW = "testOnBorrow"; static final String TEST_ON_RETURN = "testOnReturn"; static final String TEST_IDLE = "testWhileIdle"; static final String TIME_BETWEEN_EVICTIONS = "timeBetweenEvictionRunsMillis"; static final String NUM_TEST_PER_EVICTION = "numTestsPerEvictionRun"; static final String MIN_EVICTABLE_TIME = "minEvictableIdleTimeMillis"; static final String EXHAUSTED_ACTION = "whenExhaustedAction"; static final String AUTO_COMMIT = "defaultAutoCommit"; static final String READ_ONLY = "defaultReadOnly"; static final String TRANSACTION_ISOLATION = "defaultTransactionIsolation"; static final String CONNECTION_NOWRAP = "accessToUnderlyingConnectionAllowed"; static final String CATALOG = "defaultCatalog"; static final String INIT_SQL = "initSql"; // PreparedStatementPool properties static final String POOL_PS = "poolPreparedStatements"; static final String PS_MAX_ACTIVE = "ps.maxActive"; static final String PS_MAX_IDLE = "ps.maxIdle"; static final String PS_MAX_TOTAL = "ps.maxTotal"; static final String PS_MAX_WAIT = "ps.maxWait"; static final String PS_MIN_EVICTABLE_TIME = "ps.minEvictableIdleTimeMillis"; static final String PS_NUM_TEST_PER_EVICTION = "ps.numTestsPerEvictionRun"; static final String PS_TEST_ON_BORROW = "ps.testOnBorrow"; static final String PS_TEST_ON_RETURN = "ps.testOnReturn"; static final String PS_TEST_IDLE = "ps.testWhileIdle"; static final String PS_TIME_BETWEEN_EVICTIONS = "ps.timeBetweenEvictionRunsMillis"; static final String PS_EXHAUSTED_ACTION = "ps.whenExhaustedAction"; private final DBCPDataSourceProperties config; CustomDBCPDataSourceBuilder(DBCPDataSourceProperties properties) { this.config = properties; } DataSource createDataSource() { boolean connectionNoWrap = config.getBoolean(CONNECTION_NOWRAP, false); ObjectPool connectionPool = createConnectionPool(); ThreadPoolingDataSource dataSource = new ThreadPoolingDataSource(connectionPool); dataSource.setAccessToUnderlyingConnectionAllowed(connectionNoWrap); return dataSource; } private void loadDriverClass() { String driver = config.getString(DRIVER_CLASS_NAME, true); try { Class.forName(driver); } catch (ClassNotFoundException e) { throw new ConfigurationException("Error loading driver " + driver, e); } } private ObjectPool createConnectionPool() { ConnectionFactory factory = createConnectionFactory(); GenericObjectPool.Config poolConfig = createConnectionPoolConfig(); KeyedObjectPoolFactory statementPool = createPreparedStatementPool(); String validationQuery = config.getString(VALIDATION_QUERY); boolean defaultReadOnly = config.getBoolean(READ_ONLY, false); boolean defaultAutoCommit = config.getBoolean(AUTO_COMMIT, false); int defaultTransactionIsolation = config.getTransactionIsolation( TRANSACTION_ISOLATION, Connection.TRANSACTION_SERIALIZABLE); String defaultCatalog = config.getString(CATALOG); String initSql = config.getString(INIT_SQL); List<String> initSqls = null; if (initSql != null && !initSql.isEmpty()) { initSqls = new ArrayList<String>(); initSqls.add(initSql); } ObjectPool connectionPool = new GenericObjectPool(null, poolConfig); // a side effect of PoolableConnectionFactory constructor call is that newly // created factory object is assigned to "connectionPool", which is // definitely a // very confusing part of DBCP - new object is not visibly assigned to // anything, // still it is preserved... new PoolableConnectionFactory( factory, connectionPool, statementPool, validationQuery, initSqls, defaultReadOnly ? Boolean.TRUE : Boolean.FALSE, defaultAutoCommit, defaultTransactionIsolation, defaultCatalog, null); return connectionPool; } private ConnectionFactory createConnectionFactory() { loadDriverClass(); String url = config.getString(URL, true); String userName = config.getString(USER_NAME); String password = config.getString(PASSWORD); return new DriverManagerConnectionFactory(url, userName, password); } private KeyedObjectPoolFactory createPreparedStatementPool() { if (!config.getBoolean("poolPreparedStatements", false)) { return null; } // the GenericKeyedObjectPool.Config object isn't used because // although it has provision for the maxTotal parameter when // passed to the GenericKeyedObjectPoolFactory constructor // this parameter is not being properly set as a default for // creating prepared statement pools int maxActive = config.getInt(PS_MAX_ACTIVE, GenericObjectPool.DEFAULT_MAX_ACTIVE); byte whenExhaustedAction = config.getWhenExhaustedAction( PS_EXHAUSTED_ACTION, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION); long maxWait = config.getLong(PS_MAX_WAIT, GenericObjectPool.DEFAULT_MAX_WAIT); int maxIdle = config.getInt(PS_MAX_IDLE, GenericObjectPool.DEFAULT_MAX_IDLE); int maxTotal = config.getInt(PS_MAX_TOTAL, 1); boolean testOnBorrow = config.getBoolean( PS_TEST_ON_BORROW, GenericObjectPool.DEFAULT_TEST_ON_BORROW); boolean testOnReturn = config.getBoolean( PS_TEST_ON_RETURN, GenericObjectPool.DEFAULT_TEST_ON_RETURN); long timeBetweenEvictionRunsMillis = config.getLong( PS_TIME_BETWEEN_EVICTIONS, GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); int numTestsPerEvictionRun = config.getInt( PS_NUM_TEST_PER_EVICTION, GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN); long minEvictableIdleTimeMillis = config.getLong( PS_MIN_EVICTABLE_TIME, GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); boolean testWhileIdle = config .getBoolean(PS_TEST_IDLE, GenericObjectPool.DEFAULT_TEST_WHILE_IDLE); return new GenericKeyedObjectPoolFactory( null, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle); } private GenericObjectPool.Config createConnectionPoolConfig() { GenericObjectPool.Config poolConfig = new GenericObjectPool.Config(); poolConfig.maxIdle = config.getInt(MAX_IDLE, GenericObjectPool.DEFAULT_MAX_IDLE); poolConfig.minIdle = config.getInt(MIN_IDLE, GenericObjectPool.DEFAULT_MIN_IDLE); poolConfig.maxActive = config.getInt(MAX_ACTIVE, GenericObjectPool.DEFAULT_MAX_ACTIVE); poolConfig.maxWait = config.getLong(MAX_WAIT, GenericObjectPool.DEFAULT_MAX_WAIT); poolConfig.testOnBorrow = config.getBoolean( TEST_ON_BORROW, GenericObjectPool.DEFAULT_TEST_ON_BORROW); poolConfig.testOnReturn = config.getBoolean( TEST_ON_RETURN, GenericObjectPool.DEFAULT_TEST_ON_RETURN); poolConfig.testWhileIdle = config.getBoolean(TEST_IDLE, GenericObjectPool.DEFAULT_TEST_WHILE_IDLE); poolConfig.timeBetweenEvictionRunsMillis = config.getLong( TIME_BETWEEN_EVICTIONS, GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); poolConfig.numTestsPerEvictionRun = config.getInt( NUM_TEST_PER_EVICTION, GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN); poolConfig.minEvictableIdleTimeMillis = config.getLong( MIN_EVICTABLE_TIME, GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); poolConfig.whenExhaustedAction = config.getWhenExhaustedAction( EXHAUSTED_ACTION, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION); return poolConfig; } }