All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectstyle.cayenne.conf.DBCPDataSourceFactory Maven / Gradle / Ivy

Go to download

Cayenne Object Persistence Framework including all dependencies.

The newest version!
/* ====================================================================
 * 
 * The ObjectStyle Group Software License, version 1.1
 * ObjectStyle Group - http://objectstyle.org/
 * 
 * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
 * of the software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The end-user documentation included with the redistribution, if any,
 *    must include the following acknowlegement:
 *    "This product includes software developed by independent contributors
 *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, email
 *    "andrus at objectstyle dot org".
 * 
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
 *    names without prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by many
 * individuals and hosted on ObjectStyle Group web site.  For more
 * information on the ObjectStyle Group, please see
 * .
 */
package org.objectstyle.cayenne.conf;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

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.KeyedObjectPoolFactory;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.objectstyle.cayenne.ConfigurationException;

/**
 * An implementation of DataSourceFactory that instantiates a DataSource from Apache
 * Commons DBCP. Configured via a properties file specified by the location hint in the
 * modeler and under datasource attribute in cayenne.xml. Note that if location doesn't
 * have a ".properties" extension, such extension is added automatically. Sample
 * properties file:
 * 
 * 
 * 
 *  
 *   
 *    
 *     
 *           #
 *           # This file defines the configuration properties for Commons DBCP pool
 *           # which is used for Cayenne during some of the test cases. 
 *           # For more info on setting parameters see the documentation for commons
 *           # dbcp and commons pool. The following prefixes are required:
 *           # cayenne.dbcp.<param> = configure the connection pool
 *           # cayenne.dbcp.ps.<param> = configuration for the prepared connection pools
 *           # that are associated with each pooled connections
 *          
 *          
 *           #driver class to use to connect to the database
 *           cayenne.dbcp.driverClassName=net.sourceforge.jtds.jdbc.Driver
 *          
 *           #url to the database, the parameters should be part of the connection string
 *           #and not here
 *           cayenne.dbcp.url=jdbc:jtds:sqlserver://192.168.20.2:1433/x_test2;TDS=8.0
 *          
 *           #username to use to connect to the database
 *           cayenne.dbcp.username=garyj
 *          
 *           #password to use to connect to the database
 *           cayenne.dbcp.password=somepass
 *          
 *           #maximum number of active connections
 *           cayenne.dbcp.maxActive=500
 *          
 *           #minimum number of idle connections
 *           cayenne.dbcp.minIdle=10
 *          
 *           #maximum number of active connections that can remain idle in the pool
 *           cayenne.dbcp.maxIdle=10
 *          
 *           #maximum number of milliseconds to wait for a connection to be returned to the 
 *           #pool before throwing an exception when the connection is required and the pool
 *           #is exhaused of the active connections. -1 for indefinetly
 *           cayenne.dbcp.maxWait=10000
 *          
 *           #sql query to be used to validate connections from the pool. Must return
 *           #at least one row
 *           cayenne.dbcp.validationQuery=SELECT GETDATE()
 *          
 *           #should the object be validated when it is borrowed from the pool
 *           cayenne.dbcp.testOnBorrow=false
 *          
 *           #should the object be validated when it is returned to the pool
 *           cayenne.dbcp.testOnReturn=true
 *          
 *           #should the object be validated when it is idle
 *           cayenne.dbcp.testWhileIdle=true
 *          
 *           #number of milliseconds to sleep between runs of the idle object evictor thread
 *           cayenne.dbcp.timeBetweenEvictionRunsMillis=120000
 *          
 *          
 *           #number of objects to examin during each run of the idle object evictor
 *           cayenne.dbcp.numTestsPerEvictionRun=10
 *          
 *           #minimum time an object may sit idle in the pool before it is elegible for
 *           #an eviction
 *           cayenne.dbcp.minEvictableIdleTimeMillis=2000000
 *          
 *           #action to take the the pool is exhausted of all active connections
 *           #see GenericObjectPool class
 *           #this value can be set as either an int or a String the setter method
 *           #will attempt to convert the String value to it's resective representation
 *           #in the GenericObjectPool class and if successfull will use the byte 
 *           #value as the config paramter to the pool. If not the default value will
 *           #be used
 *           cayenne.dbcp.whenExhaustedAction=WHEN_EXHAUSTED_GROW
 *          
 *           #The default auto-commit state of connections created by this pool
 *           caynne.dbcp.defaultAutoCommit=false
 *          
 *           #Default read only state of connections created by the pool. Can be left
 *           #as null for driver default
 *           cayenne.dbcp.defaultReadOnly=true
 *          
 *          
 *           # Default TransactionIsolation state of connections created by this pool. This can
 *           # be either a String representation of the isolation level defined in the interface 
 *           # java.sql.Connection. Can be left as null for 
 *           # driver default
 *           cayenne.dbcp.defaultTransactionIsolation=TRANSACTION_SERIALIZABLE
 *          
 *           #If set to true the application will be able to get access to the
 *           #actual connection object which is normally wrapped by a poolable connections
 *           cayenne.dbcp.accessToUnderlyingConnectionAllowed=true
 *          
 *           #Default catalog of connections created by this pool
 *           cayenne.dbcp.defaultCatalog=someCat
 *          
 *           #Specifies whether prepared statments should be pooled
 *           cayenne.dbcp.poolPreparedStatements=true
 *          
 *          
 *           #Controlls the maximum number of objects that can be borrowed from the pool at 
 *           #one time
 *           cayenne.dbcp.ps.maxActive=500
 *          
 *           #Maximum number of idle objects in the pool
 *           cayenne.dbcp.ps.maxIdle=50
 *          
 *           #Maximum number of objects that can exist in the prepared statement pool at one time
 *           cayenne.dbcp.ps.maxTotal=600
 *          
 *          
 *           # Minimum number of milliseconds to wait for an objec the the pool of 
 *           # prepared statements is exhausted and the whenExhaustedAction is set to 
 *           # 1 (WHEN_EXHAUSTED_BLOCK)
 *           cayenne.dbcp.ps.maxWait=10000
 *          
 *          
 *           # Number of milliseconds an object can sit idle in the pool before it is 
 *           # elegible for eviction
 *           cayenne.dbcp.ps.minEvictableIdleTimeMillis=2000000
 *          
 *          
 *           #Number of idle objects that should be examined per eviction run
 *           cayenne.dbcp.ps.numTestsPerEvictionRun=20
 *          
 *          
 *           #Specifies whether objects should be validated before they are borrowed from this pool
 *           cayenne.dbcp.ps.testOnBorrow=false
 *          
 *           #Specifies whether objects should be validated when they are returned to the pool
 *           cayenne.dbcp.ps.testOnReturn=true
 *          
 *          
 *           #Specifies whether objects should be validated in the idle eviction thread
 *           cayenne.dbcp.ps.testWhileIdle=true
 *          
 *           #Specifies the time between the runs of the eviction thread
 *           cayenne.dbcp.ps.timeBetweenEvictionRunsMillis=120000
 *          
 *           # action to take when the the pool is exhausted of all active objects.
 *           # acceptable values are strings (WHEN_EXHAUSTED_FAIL, WHEN_EXHAUSTED_BLOCK (default), 
 *           # WHEN_EXHAUSTED_GROW), or their corresponding int values defined in commons-pool GenericObjectPool:
 *           cayenne.dbcp.ps.whenExhaustedAction=WHEN_EXHAUSTED_FAIL
 *      
 *     
 *    
 *   
 *  
 * 
* * @since 1.2 * @author Gary Jarrel */ public class DBCPDataSourceFactory implements DataSourceFactory { private static final Logger logger = Logger.getLogger(DBCPDataSourceFactory.class); /** * Suffix of the properties file */ private static final String SUFFIX = ".properties"; /** * All the properties in the configuration properties file should be prefixed with * this prefix, namely cayenne.dbcp. The config parameter as set out in commons dbcp * configuration should follow this prefix. */ public static final String PROPERTY_PREFIX = "cayenne.dbcp."; /** * The the properties in the configuration file, related to Prepared Statement pooling * and used to configure KeyedObjectPoolFactory are followed by this * prefix. This constants is set to PROPERTY_PREFIX + ps. */ public static final String PS_PROPERTY_PREFIX = PROPERTY_PREFIX + "ps."; protected Configuration parentConfiguration; public void initializeWithParentConfiguration(Configuration parentConfiguration) { this.parentConfiguration = parentConfiguration; } /** * @deprecated since 1.2 */ public DataSource getDataSource(String location, Level logLevel) throws Exception { return getDataSource(location); } /** * Creates a DBCP PoolingDataSource * * @return DataSource which is an instance of * PoolingDataSource * @throws Exception */ public DataSource getDataSource(String location) throws Exception { if (!location.endsWith(SUFFIX)) { location = location.concat(SUFFIX); } logger.info("Loading DBCP properties from " + location); Properties properties = loadProperties(location); logger.info("Loaded DBCP properties: " + properties); loadDriverClass(properties); // build and assemble parts of DBCP DataSource... ConnectionFactory factory = createConnectionFactory(properties); KeyedObjectPoolFactory statementPool = createStatementPool(properties); GenericObjectPool.Config config = createPoolConfig(properties); // PoolableConnectionFactory properties String validationQuery = stringProperty(properties, "validationQuery"); boolean defaultReadOnly = booleanProperty(properties, "defaultReadOnly", false); boolean defaultAutoCommit = booleanProperty( properties, "defaultAutoCommit", false); int defaultTransactionIsolation = defaultTransactionIsolation( properties, "defaultTransactionIsolation", Connection.TRANSACTION_SERIALIZABLE); String defaultCatalog = stringProperty(properties, "defaultCatalog"); // 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... ObjectPool connectionPool = new GenericObjectPool(null, config); new PoolableConnectionFactory( factory, connectionPool, statementPool, validationQuery, defaultReadOnly ? Boolean.TRUE : Boolean.FALSE, defaultAutoCommit, defaultTransactionIsolation, defaultCatalog, null); PoolingDataSource dataSource = new PoolingDataSource(connectionPool); dataSource.setAccessToUnderlyingConnectionAllowed(booleanProperty( properties, "accessToUnderlyingConnectionAllowed", false)); return dataSource; } /** * Loads driver class into driver manager. */ void loadDriverClass(Properties properties) throws Exception { String driver = stringProperty(properties, "driverClassName"); logger.info("loading JDBC driver class: " + driver); if (driver == null) { throw new NullPointerException("No value for required property: " + PROPERTY_PREFIX + "driverClassName"); } Class.forName(driver); } KeyedObjectPoolFactory createStatementPool(Properties properties) throws Exception { if (!booleanProperty(properties, "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 = intProperty( properties, "ps.maxActive", GenericObjectPool.DEFAULT_MAX_ACTIVE); byte whenExhaustedAction = whenExhaustedAction( properties, "ps.whenExhaustedAction", GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION); long maxWait = longProperty( properties, "ps.maxWait", GenericObjectPool.DEFAULT_MAX_WAIT); int maxIdle = intProperty( properties, "ps.maxIdle", GenericObjectPool.DEFAULT_MAX_IDLE); int maxTotal = intProperty(properties, "ps.maxTotal", 1); boolean testOnBorrow = booleanProperty( properties, "ps.testOnBorrow", GenericObjectPool.DEFAULT_TEST_ON_BORROW); boolean testOnReturn = booleanProperty( properties, "ps.testOnReturn", GenericObjectPool.DEFAULT_TEST_ON_RETURN); long timeBetweenEvictionRunsMillis = longProperty( properties, "ps.timeBetweenEvictionRunsMillis", GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); int numTestsPerEvictionRun = intProperty( properties, "ps.numTestsPerEvictionRun", GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN); long minEvictableIdleTimeMillis = longProperty( properties, "ps.minEvictableIdleTimeMillis", GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); boolean testWhileIdle = booleanProperty( properties, "ps.testWhileIdle", GenericObjectPool.DEFAULT_TEST_WHILE_IDLE); return new GenericKeyedObjectPoolFactory( null, maxActive, whenExhaustedAction, maxWait, maxIdle, maxTotal, testOnBorrow, testOnReturn, timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle); } ConnectionFactory createConnectionFactory(Properties properties) { String url = stringProperty(properties, "url"); String userName = stringProperty(properties, "username"); String password = stringProperty(properties, "password"); // sanity check if (url == null) { throw new NullPointerException("No value for required property: " + PROPERTY_PREFIX + "url"); } return new DriverManagerConnectionFactory(url, userName, password); } GenericObjectPool.Config createPoolConfig(Properties properties) throws Exception { GenericObjectPool.Config config = new GenericObjectPool.Config(); config.maxIdle = intProperty( properties, "maxIdle", GenericObjectPool.DEFAULT_MAX_IDLE); config.minIdle = intProperty( properties, "minIdle", GenericObjectPool.DEFAULT_MIN_IDLE); config.maxActive = intProperty( properties, "maxActive", GenericObjectPool.DEFAULT_MAX_ACTIVE); config.maxWait = longProperty( properties, "maxWait", GenericObjectPool.DEFAULT_MAX_WAIT); config.testOnBorrow = booleanProperty( properties, "testOnBorrow", GenericObjectPool.DEFAULT_TEST_ON_BORROW); config.testOnReturn = booleanProperty( properties, "testOnReturn", GenericObjectPool.DEFAULT_TEST_ON_RETURN); config.testWhileIdle = booleanProperty( properties, "testWhileIdle", GenericObjectPool.DEFAULT_TEST_WHILE_IDLE); config.timeBetweenEvictionRunsMillis = longProperty( properties, "timeBetweenEvictionRunsMillis", GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); config.numTestsPerEvictionRun = intProperty( properties, "numTestsPerEvictionRun", GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN); config.minEvictableIdleTimeMillis = longProperty( properties, "minEvictableIdleTimeMillis", GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); config.whenExhaustedAction = whenExhaustedAction( properties, "whenExhaustedAction", GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION); return config; } Properties loadProperties(String location) throws IOException { Properties properties = new Properties(); InputStream in = getInputStream(location); if (in == null) { throw new ConfigurationException("DBCP properties file not found: " + location); } try { properties.load(in); } finally { try { in.close(); } catch (IOException ignore) { } } return properties; } int defaultTransactionIsolation( Properties properties, String property, int defaultValue) { String value = stringProperty(properties, property); if (value == null) { return defaultValue; } // try int... try { return Integer.parseInt(value); } catch (NumberFormatException nfex) { // try symbolic try { return Connection.class.getField(value).getInt(null); } catch (Throwable th) { throw new ConfigurationException( "Invalid 'defaultTransactionIsolation': " + value); } } } byte whenExhaustedAction(Properties properties, String property, byte defaultValue) throws Exception { String value = stringProperty(properties, property); if (value == null) { return defaultValue; } // try byte... try { return Byte.parseByte(value); } catch (NumberFormatException nfex) { // try symbolic try { return GenericObjectPool.class.getField(value).getByte(null); } catch (Throwable th) { throw new ConfigurationException("Invalid 'whenExhaustedAction': " + value); } } } String stringProperty(Properties properties, String property) { return properties.getProperty(PROPERTY_PREFIX + property); } boolean booleanProperty(Properties properties, String property, boolean defaultValue) { String value = stringProperty(properties, property); return (value != null) ? "true".equalsIgnoreCase(stringProperty( properties, property)) : defaultValue; } int intProperty(Properties properties, String property, int defaultValue) { String value = stringProperty(properties, property); try { return (value != null) ? Integer.parseInt(value) : defaultValue; } catch (NumberFormatException nfex) { return defaultValue; } } long longProperty(Properties properties, String property, long defaultValue) { String value = stringProperty(properties, property); try { return (value != null) ? Long.parseLong(value) : defaultValue; } catch (NumberFormatException nfex) { return defaultValue; } } byte byteProperty(Properties properties, String property, byte defaultValue) { String value = stringProperty(properties, property); try { return (value != null) ? Byte.parseByte(value) : defaultValue; } catch (NumberFormatException nfex) { return defaultValue; } } /** * Returns an input stream for the file corresponding to location. */ InputStream getInputStream(String location) { if (this.parentConfiguration == null) { throw new ConfigurationException( "No parent Configuration set - cannot continue."); } return this.parentConfiguration.getResourceLocator().findResourceStream(location); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy