public class PooledDatasourceHelper {
public static PoolingDataSource setupPooledDataSource( IDatabaseConnection databaseConnection )
throws DBDatasourceServiceException {
PoolingDataSource poolingDataSource = null;
String driverClass = null;
String url = null;
try {
ICacheManager cacheManager = PentahoSystem.getCacheManager( null );
IDatabaseDialectService databaseDialectService = PentahoSystem.get( IDatabaseDialectService.class );
if ( databaseDialectService == null ) {
throw new DBDatasourceServiceException( Messages.getInstance().getErrorString(
"PooledDatasourceHelper.ERROR_0005_UNABLE_TO_POOL_DATASOURCE_NO_DIALECT_SERVICE",
databaseConnection.getName() ) );
}
IDatabaseDialect dialect = databaseDialectService.getDialect( databaseConnection );
if ( dialect == null || dialect.getDatabaseType() == null ) {
throw new DBDatasourceServiceException( Messages.getInstance().getErrorString(
"PooledDatasourceHelper.ERROR_0004_UNABLE_TO_POOL_DATASOURCE_NO_DIALECT", databaseConnection.getName() ) );
}
if ( databaseConnection.getDatabaseType().getShortName().equals( "GENERIC" ) ) { //$NON-NLS-1$
driverClass = databaseConnection.getAttributes().get( GenericDatabaseDialect.ATTRIBUTE_CUSTOM_DRIVER_CLASS );
if ( StringUtils.isEmpty( driverClass ) ) {
throw new DBDatasourceServiceException( Messages.getInstance().getErrorString(
"PooledDatasourceHelper.ERROR_0006_UNABLE_TO_POOL_DATASOURCE_NO_CLASSNAME", databaseConnection.getName() ) );
}
} else {
driverClass = dialect.getNativeDriver();
if ( StringUtils.isEmpty( driverClass ) ) {
throw new DBDatasourceServiceException( Messages.getInstance().getErrorString(
"PooledDatasourceHelper.ERROR_0007_UNABLE_TO_POOL_DATASOURCE_NO_DRIVER", databaseConnection.getName() ) );
}
}
try {
url = dialect.getURLWithExtraOptions( databaseConnection );
} catch ( DatabaseDialectException e ) {
url = null;
}
// Read default connection pooling parameter
String maxdleConn = PentahoSystem.getSystemSetting( "dbcp-defaults/max-idle-conn", null ); //$NON-NLS-1$
String minIdleConn = PentahoSystem.getSystemSetting( "dbcp-defaults/min-idle-conn", null ); //$NON-NLS-1$
String maxActConn = PentahoSystem.getSystemSetting( "dbcp-defaults/max-act-conn", null ); //$NON-NLS-1$
String validQuery = null;
String whenExhaustedAction = PentahoSystem.getSystemSetting( "dbcp-defaults/when-exhausted-action", null ); //$NON-NLS-1$
String wait = PentahoSystem.getSystemSetting( "dbcp-defaults/wait", null ); //$NON-NLS-1$
String testWhileIdleValue = PentahoSystem.getSystemSetting( "dbcp-defaults/test-while-idle", null ); //$NON-NLS-1$
String testOnBorrowValue = PentahoSystem.getSystemSetting( "dbcp-defaults/test-on-borrow", null ); //$NON-NLS-1$
String testOnReturnValue = PentahoSystem.getSystemSetting( "dbcp-defaults/test-on-return", null ); //$NON-NLS-1$
// property initialization
boolean testWhileIdle =
!StringUtil.isEmpty( testWhileIdleValue ) ? Boolean.parseBoolean( testWhileIdleValue ) : false;
boolean testOnBorrow =
!StringUtil.isEmpty( testOnBorrowValue ) ? Boolean.parseBoolean( testOnBorrowValue ) : false;
boolean testOnReturn =
!StringUtil.isEmpty( testOnReturnValue ) ? Boolean.parseBoolean( testOnReturnValue ) : false;
int maxActiveConnection = !StringUtil.isEmpty( maxActConn ) ? Integer.parseInt( maxActConn ) : -1;
long waitTime = !StringUtil.isEmpty( wait ) ? Integer.parseInt( wait ) : -1;
byte whenExhaustedActionType =
!StringUtil.isEmpty( whenExhaustedAction ) ? Byte.parseByte( whenExhaustedAction )
: GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
int minIdleConnection = !StringUtil.isEmpty( minIdleConn ) ? Integer.parseInt( minIdleConn ) : -1;
int maxIdleConnection = !StringUtil.isEmpty( maxdleConn ) ? Integer.parseInt( maxdleConn ) : -1;
// setting properties according to user specifications
Map<String, String> attributes = databaseConnection.getConnectionPoolingProperties();
if ( attributes.containsKey( IDBDatasourceService.MAX_ACTIVE_KEY )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.MAX_ACTIVE_KEY ) ) ) {
maxActiveConnection = Integer.parseInt( attributes.get( IDBDatasourceService.MAX_ACTIVE_KEY ) );
}
if ( attributes.containsKey( IDBDatasourceService.MAX_WAIT_KEY )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.MAX_WAIT_KEY ) ) ) {
waitTime = Integer.parseInt( attributes.get( IDBDatasourceService.MAX_WAIT_KEY ) );
}
if ( attributes.containsKey( IDBDatasourceService.MIN_IDLE_KEY )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.MIN_IDLE_KEY ) ) ) {
minIdleConnection = Integer.parseInt( attributes.get( IDBDatasourceService.MIN_IDLE_KEY ) );
}
if ( attributes.containsKey( IDBDatasourceService.MAX_IDLE_KEY )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.MAX_IDLE_KEY ) ) ) {
maxIdleConnection = Integer.parseInt( attributes.get( IDBDatasourceService.MAX_IDLE_KEY ) );
}
if ( attributes.containsKey( IDBDatasourceService.QUERY_KEY ) ) {
validQuery = attributes.get( IDBDatasourceService.QUERY_KEY );
}
if ( attributes.containsKey( IDBDatasourceService.TEST_ON_BORROW ) ) {
testOnBorrow = Boolean.parseBoolean( attributes.get( IDBDatasourceService.TEST_ON_BORROW ) );
}
if ( attributes.containsKey( IDBDatasourceService.TEST_ON_RETURN ) ) {
testOnReturn = Boolean.parseBoolean( attributes.get( IDBDatasourceService.TEST_ON_RETURN ) );
}
if ( attributes.containsKey( IDBDatasourceService.TEST_WHILE_IDLE ) ) {
testWhileIdle = Boolean.parseBoolean( attributes.get( IDBDatasourceService.TEST_WHILE_IDLE ) );
}
poolingDataSource = new PoolingDataSource();
Class.forName( driverClass );
// As the name says, this is a generic pool; it returns basic Object-class objects.
GenericObjectPool pool = new GenericObjectPool( null );
// if removedAbandoned = true, then an AbandonedObjectPool object will take GenericObjectPool's place
if ( attributes.containsKey( IDBDatasourceService.REMOVE_ABANDONED )
&& true == Boolean.parseBoolean( attributes.get( IDBDatasourceService.REMOVE_ABANDONED ) ) ) {
AbandonedConfig config = new AbandonedConfig();
config.setRemoveAbandoned( Boolean.parseBoolean( attributes.get( IDBDatasourceService.REMOVE_ABANDONED ) ) );
if ( attributes.containsKey( IDBDatasourceService.LOG_ABANDONED ) ) {
config.setLogAbandoned( Boolean.parseBoolean( attributes.get( IDBDatasourceService.LOG_ABANDONED ) ) );
}
if ( attributes.containsKey( IDBDatasourceService.REMOVE_ABANDONED_TIMEOUT )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.REMOVE_ABANDONED_TIMEOUT ) ) ) {
config.setRemoveAbandonedTimeout( Integer.parseInt( attributes
.get( IDBDatasourceService.REMOVE_ABANDONED_TIMEOUT ) ) );
}
pool = new AbandonedObjectPool( null, config );
}
pool.setWhenExhaustedAction( whenExhaustedActionType );
// Tuning the connection pool
pool.setMaxActive( maxActiveConnection );
pool.setMaxIdle( maxIdleConnection );
pool.setMaxWait( waitTime );
pool.setMinIdle( minIdleConnection );
pool.setTestWhileIdle( testWhileIdle );
pool.setTestOnReturn( testOnReturn );
pool.setTestOnBorrow( testOnBorrow );
pool.setTestWhileIdle( testWhileIdle );
if ( attributes.containsKey( IDBDatasourceService.TIME_BETWEEN_EVICTION_RUNS_MILLIS )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.TIME_BETWEEN_EVICTION_RUNS_MILLIS ) ) ) {
pool.setTimeBetweenEvictionRunsMillis( Long.parseLong( attributes
.get( IDBDatasourceService.TIME_BETWEEN_EVICTION_RUNS_MILLIS ) ) );
}
/*
* ConnectionFactory creates connections on behalf of the pool. Here, we use the DriverManagerConnectionFactory
* because that essentially uses DriverManager as the source of connections.
*/
ConnectionFactory factory =
new DriverManagerConnectionFactory( url, databaseConnection.getUsername(), databaseConnection.getPassword() );
boolean defaultReadOnly =
attributes.containsKey( IDBDatasourceService.DEFAULT_READ_ONLY ) ? Boolean.parseBoolean( attributes
.get( IDBDatasourceService.TEST_WHILE_IDLE ) ) : false; // default to false
boolean defaultAutoCommit =
attributes.containsKey( IDBDatasourceService.DEFAULT_AUTO_COMMIT ) ? Boolean.parseBoolean( attributes
.get( IDBDatasourceService.DEFAULT_AUTO_COMMIT ) ) : true; // default to true
KeyedObjectPoolFactory kopf = null;
if ( attributes.containsKey( IDBDatasourceService.POOL_PREPARED_STATEMENTS )
&& true == Boolean.parseBoolean( attributes.get( IDBDatasourceService.POOL_PREPARED_STATEMENTS ) ) ) {
int maxOpenPreparedStatements = -1; // unlimited
if ( attributes.containsKey( IDBDatasourceService.MAX_OPEN_PREPARED_STATEMENTS )
&& NumberUtils.isNumber( attributes.get( IDBDatasourceService.MAX_OPEN_PREPARED_STATEMENTS ) ) ) {
maxOpenPreparedStatements =
Integer.parseInt( attributes.get( IDBDatasourceService.MAX_OPEN_PREPARED_STATEMENTS ) );
}
kopf =
new GenericKeyedObjectPoolFactory( null, pool.getMaxActive(), pool.getWhenExhaustedAction(), pool
.getMaxWait(), pool.getMaxIdle(), maxOpenPreparedStatements );
}
/*
* Puts pool-specific wrappers on factory connections. For clarification: "[PoolableConnection]Factory," not
* "Poolable[ConnectionFactory]."
*/
PoolableConnectionFactory pcf = new PoolableConnectionFactory( factory, // ConnectionFactory
pool, // ObjectPool
kopf, // KeyedObjectPoolFactory
validQuery, // String (validation query)
defaultReadOnly, // boolean (default to read-only?)
defaultAutoCommit // boolean (default to auto-commit statements?)
);
if ( attributes.containsKey( IDBDatasourceService.DEFAULT_TRANSACTION_ISOLATION )
&& !IDBDatasourceService.TRANSACTION_ISOLATION_NONE_VALUE.equalsIgnoreCase( attributes
.get( IDBDatasourceService.DEFAULT_TRANSACTION_ISOLATION ) ) ) {
Isolation isolationLevel =
Isolation.valueOf( attributes.get( IDBDatasourceService.DEFAULT_TRANSACTION_ISOLATION ) );
if ( isolationLevel != null ) {
pcf.setDefaultTransactionIsolation( isolationLevel.value() );
}
}
if ( attributes.containsKey( IDBDatasourceService.DEFAULT_CATALOG ) ) {
pcf.setDefaultCatalog( attributes.get( IDBDatasourceService.DEFAULT_CATALOG ) );
}
/*
* initialize the pool to X connections
*/
Logger.debug( PooledDatasourceHelper.class, "Pool defaults to " + maxActiveConnection + " max active/"
+ maxIdleConnection + "max idle" + "with " + waitTime + "wait time"//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ " idle connections." ); //$NON-NLS-1$
for ( int i = 0; i < maxIdleConnection; ++i ) {
pool.addObject();
}
Logger.debug( PooledDatasourceHelper.class, "Pool now has " + pool.getNumActive() + " active/"
+ pool.getNumIdle() + " idle connections." ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
/*
* All of this is wrapped in a DataSource, which client code should already know how to handle (since it's the
* same class of object they'd fetch via the container's JNDI tree
*/
poolingDataSource.setPool( pool );
if ( attributes.containsKey( IDBDatasourceService.ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED ) ) {
poolingDataSource.setAccessToUnderlyingConnectionAllowed( Boolean.parseBoolean( attributes
.get( IDBDatasourceService.ACCESS_TO_UNDERLYING_CONNECTION_ALLOWED ) ) );
}
// store the pool, so we can get to it later
cacheManager.putInRegionCache( IDBDatasourceService.JDBC_POOL, databaseConnection.getName(), pool );