/**
* Copyright 2010 Wallace Wadge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jolbox.bonecp;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.sql.Connection;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import static com.google.common.base.Preconditions.*;
import com.jolbox.bonecp.hooks.ConnectionHook;
/**
* Configuration class.
*
* @author wallacew
*/
public class BoneCPConfig implements BoneCPConfigMBean, Cloneable, Serializable {
/** Constant. */
private static final String USER = "user";
/** Constant. */
private static final String PASSWORD = "password";
/** Serialization UID. */
private static final long serialVersionUID = 6090570773474131622L;
/** For toString(). */
private static final String CONFIG_TOSTRING = "JDBC URL = %s, Username = %s, partitions = %d, max (per partition) = %d, min (per partition) = %d, idle max age = %d min, idle test period = %d min, strategy = %s";
/** For toString(). */
private static final String CONFIG_DS_TOSTRING = "JDBC URL = (via datasource bean), Username = (via datasource bean), partitions = %d, max (per partition) = %d, min (per partition) = %d, idle max age = %d min, idle test period = %d min, strategy = %s";
/** Logger class. */
private static final Logger logger = LoggerFactory.getLogger(BoneCPConfig.class);
/** Min number of connections per partition. */
private int minConnectionsPerPartition = 1;
/** Max number of connections per partition. */
private int maxConnectionsPerPartition = 2;
/** Number of new connections to create in 1 batch. */
private int acquireIncrement = 2;
/** Number of partitions. */
private int partitionCount = 1;
/** DB connection string. */
private String jdbcUrl;
/** User name to use. */
private String username;
/** Password to use. */
private String password;
/** Connections older than this are sent a keep-alive statement. */
private long idleConnectionTestPeriodInSeconds = 240*60;
/** Maximum age of an unused connection before it is closed off. */
private long idleMaxAgeInSeconds = 60*60;
/** SQL statement to use for keep-alive/test of connection. */
private String connectionTestStatement;
/** Min no of prepared statements to cache. */
private int statementsCacheSize = 0;
/** No of statements that can be cached per connection. Deprecated. */
private int statementsCachedPerConnection = 0;
/** Number of release-connection helper threads to create per partition. */
private int releaseHelperThreads = 0;
/** Number of statement release helper threads to create. */
private int statementReleaseHelperThreads = 0;
/** Hook class (external). */
private ConnectionHook connectionHook;
/** Query to send once per connection to the database. */
private String initSQL;
/** If set to true, create a new thread that monitors a connection and displays warnings if application failed to
* close the connection. FOR DEBUG PURPOSES ONLY!
*/
private boolean closeConnectionWatch;
/** If set to true, log SQL statements being executed. */
private boolean logStatementsEnabled;
/** After attempting to acquire a connection and failing, wait for this value before attempting to acquire a new connection again. */
private long acquireRetryDelayInMs=7000;
/** After attempting to acquire a connection and failing, try to connect these many times before giving up. */
private int acquireRetryAttempts=5;
/** If set to true, the connection pool will remain empty until the first connection is obtained. */
private boolean lazyInit;
/** If set to true, stores all activity on this connection to allow for replaying it again. */
private boolean transactionRecoveryEnabled;
/** Connection hook class name. */
private String connectionHookClassName;
/** Classloader to use when loading the JDBC driver. */
private ClassLoader classLoader = this.getClassLoader();
/** Name of the pool for JMX and thread names. */
private String poolName;
/** Set to true to disable JMX. */
private boolean disableJMX;
/** If set, use datasourceBean.getConnection() to obtain a new connection. */
private DataSource datasourceBean;
/** Queries taking longer than this limit to execute are logged. */
private long queryExecuteTimeLimitInMs = 0;
/** Create more connections when we hit x% of our possible number of connections. */
private int poolAvailabilityThreshold = 0;
/** Disable connection tracking. */
private boolean disableConnectionTracking;
/** Used when the alternate way of obtaining a connection is required */
@VisibleForTesting protected Properties driverProperties;
/** Time to wait before a call to getConnection() times out and returns an error. */
private long connectionTimeoutInMs = 0;
/** Time in ms to wait for close connection watch thread. */
private long closeConnectionWatchTimeoutInMs = 0;
/** A connection older than maxConnectionAge will be destroyed and purged from the pool. */
private long maxConnectionAgeInSeconds = 0;
/** Config property. */
private String configFile;
/** Queue mode. Values currently understood are FIFO and LIFO. */
private String serviceOrder = "FIFO";
/** If true, keep track of some statistics. */
private boolean statisticsEnabled;
/** The default auto-commit state of created connections. */
private boolean defaultAutoCommit = true;
/** The default read-only state of created connections. */
private boolean defaultReadOnly;
/** The default transaction isolation state of created connections. */
private String defaultTransactionIsolation;
/** The default catalog state of created connections. */
private String defaultCatalog;
/** The parsed transaction isolation value. Default = driver value*/
private int defaultTransactionIsolationValue = -1;
/** If true, stop caring about username/password when obtaining raw connections. */
private boolean externalAuth;
/** If true, try to unregister the JDBC driver when pool is shutdown. */
private boolean deregisterDriverOnClose;
/** If true, return null on connection timeout rather than throw an exception. */
private boolean nullOnConnectionTimeout;
/** If true, issue a reset (rollback) on connection close in case client forgot it. */
private boolean resetConnectionOnClose;
/** Detect uncommitted transactions. If true, and resetConnectionOnClose is also true, the pool will print out a stack
* trace of the location where you had a connection that specified setAutoCommit(false)
* but then forgot to call commit/rollback before closing it off. This feature is intended
* for debugging only.*/
private boolean detectUnresolvedTransactions;
/** Determines pool operation Recognised strategies are: DEFAULT, CACHED. */
private String poolStrategy = "DEFAULT";
/** If true, track statements and close them if application forgot to do so. See also:
* detectUnclosedStatements. */
private boolean closeOpenStatements;
/** If true, print out a stack trace of where a statement was opened but not closed before
* the connection was closed. See also: closeOpenStatements. */
private boolean detectUnclosedStatements;
/** If set, pool will call this for every new connection that's created. */
private Properties clientInfo;
/** Returns the name of the pool for JMX and thread names.
* @return a pool name.
*/
public String getPoolName() {
return this.poolName;
}
/** Sets the name of the pool for JMX and thread names.
* @param poolName to set.
*/
public void setPoolName(String poolName) {
this.poolName = checkNotNull(poolName);
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getMinConnectionsPerPartition()
*/
public int getMinConnectionsPerPartition() {
return this.minConnectionsPerPartition;
}
/**
* Sets the minimum number of connections that will be contained in every partition. Also refer
* to {@link #setPoolAvailabilityThreshold(int)}.
*
* @param minConnectionsPerPartition number of connections
*/
public void setMinConnectionsPerPartition(int minConnectionsPerPartition) {
this.minConnectionsPerPartition = minConnectionsPerPartition;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getMaxConnectionsPerPartition()
*/
public int getMaxConnectionsPerPartition() {
return this.maxConnectionsPerPartition;
}
/**
* Sets the maximum number of connections that will be contained in every partition.
* Setting this to 5 with 3 partitions means you will have 15 unique connections to the database.
* Note that the connection pool will not create all these connections in one go but rather start off
* with minConnectionsPerPartition and gradually increase connections as required.
*
* @param maxConnectionsPerPartition number of connections.
*/
public void setMaxConnectionsPerPartition(int maxConnectionsPerPartition) {
this.maxConnectionsPerPartition = maxConnectionsPerPartition;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getAcquireIncrement()
*/
public int getAcquireIncrement() {
return this.acquireIncrement;
}
/**
* Sets the acquireIncrement property.
*
* When the available connections are about to run out, BoneCP will dynamically create new ones in batches.
* This property controls how many new connections to create in one go (up to a maximum of maxConnectionsPerPartition).
* <p>Note: This is a per partition setting.
*
* @param acquireIncrement value to set.
*/
public void setAcquireIncrement(int acquireIncrement) {
this.acquireIncrement = acquireIncrement;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getPartitionCount()
*/
public int getPartitionCount() {
return this.partitionCount;
}
/**
* Sets number of partitions to use.
*
* In order to reduce lock contention and thus improve performance,
* each incoming connection request picks off a connection from a pool that has thread-affinity,
* i.e. pool[threadId % partition_count]. The higher this number, the better your performance will be for the case
* when you have plenty of short-lived threads. Beyond a certain threshold (approx 4), maintenance of these pools will start
* to have a negative effect on performance (and only for the case when connections on a partition start running out).
* Has no effect in a CACHED strategy.
*
* <p>Default: 1, minimum: 1, recommended: 2-4 (but very app specific)
*
* @param partitionCount to set
*/
public void setPartitionCount(int partitionCount) {
this.partitionCount = partitionCount;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getJdbcUrl()
*/
public String getJdbcUrl() {
return this.jdbcUrl;
}
/**
* Sets the JDBC connection URL.
*
* @param jdbcUrl to set
*/
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getUsername()
*/
public String getUsername() {
return this.username;
}
/**
* Sets username to use for connections.
*
* @param username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Sets username to use for connections. Just delegates to setUsername for clients hardcoded
* with "setUser" instead.
*
* @param username to set
*/
public void setUser(String username) {
setUsername(username);
}
/** Just delegates to getUsername for clients hardcoded to "getUser".
* @return configured username
*/
public String getUser() {
return getUsername();
}
/**
* Gets password to use for connections
*
* @return password
*/
public String getPassword() {
return this.password;
}
/**
* Sets password to use for connections.
*
* @param password to set.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Deprecated.
*
* @deprecated Please use {@link #getIdleConnectionTestPeriodInMinutes()} instead.
* @return idleConnectionTest
*/
@Deprecated
public long getIdleConnectionTestPeriod() {
logger.warn("Please use getIdleConnectionTestPeriodInMinutes in place of getIdleConnectionTestPeriod. This method has been deprecated.");
return getIdleConnectionTestPeriodInMinutes();
}
/**
* Sets the idleConnectionTestPeriod in minutes
*
* @deprecated Please use {@link #setIdleConnectionTestPeriodInMinutes(long)} or {@link #setIdleConnectionTestPeriod(long, TimeUnit)} instead
* @param idleConnectionTestPeriod to set in minutes
*/
@Deprecated
public void setIdleConnectionTestPeriod(long idleConnectionTestPeriod) {
logger.warn("Please use setIdleConnectionTestPeriodInMinutes in place of setIdleConnectionTestPeriod. This method has been deprecated.");
setIdleConnectionTestPeriod(idleConnectionTestPeriod*60, TimeUnit.SECONDS);
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getIdleConnectionTestPeriodInMinutes()
*/
public long getIdleConnectionTestPeriodInMinutes() {
return this.idleConnectionTestPeriodInSeconds / 60;
}
/**
* Returns the idleConnectionTestPeriod with the specified granularity.
*
* @param timeUnit time granularity
* @return Idle Connection test period
*/
public long getIdleConnectionTestPeriod(TimeUnit timeUnit) {
return timeUnit.convert(this.idleConnectionTestPeriodInSeconds, TimeUnit.SECONDS);
}
/**
* Sets the idleConnectionTestPeriod.
*
* This sets the time (in minutes), for a connection to remain idle before sending
* a test query to the DB. This is useful to prevent a DB from timing out connections
* on its end. Do not use aggressive values here!
*
*
* <p>Default: 240 min, set to 0 to disable
*
* @param idleConnectionTestPeriod to set
*/
public void setIdleConnectionTestPeriodInMinutes(long idleConnectionTestPeriod) {
// we use TimeUnit.SECONDS instead of TimeUnit.MINUTES because it's not supported
// by JDK5
setIdleConnectionTestPeriod(idleConnectionTestPeriod*60, TimeUnit.SECONDS);
}
/**
* Sets the idleConnectionTestPeriod.
*
* This sets the time (in seconds), for a connection to remain idle before sending
* a test query to the DB. This is useful to prevent a DB from timing out connections
* on its end. Do not use aggressive values here!
*
*
* <p>Default: 240 min, set to 0 to disable
*
* @param idleConnectionTestPeriod to set
*/
public void setIdleConnectionTestPeriodInSeconds(long idleConnectionTestPeriod) {
setIdleConnectionTestPeriod(idleConnectionTestPeriod, TimeUnit.SECONDS);
}
/** Wrapper method for idleConnectionTestPeriod for easier programmatic access.
* @param idleConnectionTestPeriod time for a connection to remain idle before sending a test
* query to the DB.
* @param timeUnit Time granularity of given parameter.
*/
public void setIdleConnectionTestPeriod(long idleConnectionTestPeriod, TimeUnit timeUnit) {
this.idleConnectionTestPeriodInSeconds = TimeUnit.SECONDS.convert(idleConnectionTestPeriod, checkNotNull(timeUnit));
}
/** Deprecated.
* @return idleMaxAge in minutes
* @deprecated Use {@link #getIdleMaxAgeInMinutes()} instead
*/
@Deprecated
public long getIdleMaxAge() {
logger.warn("Please use getIdleMaxAgeInMinutes in place of getIdleMaxAge. This method has been deprecated.");
return getIdleMaxAgeInMinutes();
}
/**
* Returns the idleMaxAge with the specified granularity.
*
* @param timeUnit time granularity
* @return idleMaxAge value
*/
public long getIdleMaxAge(TimeUnit timeUnit) {
return timeUnit.convert(this.idleMaxAgeInSeconds, TimeUnit.SECONDS);
}
/** Returns the idleMaxAge currently set.
* @return idleMaxAge in minutes
*/
public long getIdleMaxAgeInMinutes() {
return this.idleMaxAgeInSeconds / 60;
}
/**
* Deprecated.
*
* @param idleMaxAge to set
* @deprecated Use {@link #setIdleMaxAgeInMinutes(long)} or {@link #setIdleMaxAge(long, TimeUnit)} instead.
*/
@Deprecated
public void setIdleMaxAge(long idleMaxAge) {
logger.warn("Please use setIdleMaxAgeInMinutes in place of setIdleMaxAge. This method has been deprecated.");
setIdleMaxAgeInMinutes(idleMaxAge);
}
/**
* Sets Idle max age (in min).
*
* The time (in minutes), for a connection to remain unused before it is closed off. Do not use aggressive values here!
*
* <p>Default: 60 minutes, set to 0 to disable.
*
* @param idleMaxAge to set
*/
public void setIdleMaxAgeInMinutes(long idleMaxAge) {
setIdleMaxAge(idleMaxAge*60, TimeUnit.SECONDS);
}
/**
* Sets Idle max age (in seconds).
*
* The time (in seconds), for a connection to remain unused before it is closed off. Do not use aggressive values here!
*
* <p>Default: 60 minutes, set to 0 to disable.
*
* @param idleMaxAge to set
*/
public void setIdleMaxAgeInSeconds(long idleMaxAge) {
setIdleMaxAge(idleMaxAge, TimeUnit.SECONDS);
}
/** Sets Idle max age.
*
* The time, for a connection to remain unused before it is closed off. Do not use aggressive values here!
* @param idleMaxAge time after which a connection is closed off
* @param timeUnit idleMaxAge time granularity.
*/
public void setIdleMaxAge(long idleMaxAge, TimeUnit timeUnit) {
this.idleMaxAgeInSeconds = TimeUnit.SECONDS.convert(idleMaxAge, checkNotNull(timeUnit));
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getConnectionTestStatement()
*/
public String getConnectionTestStatement() {
return this.connectionTestStatement;
}
/**
*Sets the connection test statement.
*
*The query to send to the DB to maintain keep-alives and test for dead connections.
*This is database specific and should be set to a query that consumes the minimal amount of load on the server.
*Examples: MySQL: "/* ping *\/ SELECT 1", PostgreSQL: "SELECT NOW()".
*If you do not set this, then BoneCP will issue a metadata request instead that should work on all databases but is probably slower.
*
* (Note: In MySQL, prefixing the statement by /* ping *\/ makes the driver issue 1 fast packet instead. See
* http://blogs.sun.com/SDNChannel/entry/mysql_tips_for_java_developers )
*<p>Default: Use metadata request
*
* @param connectionTestStatement to set.
*/
public void setConnectionTestStatement(String connectionTestStatement) {
this.connectionTestStatement = checkNotNull(connectionTestStatement);
}
/** Deprecated. Use getStatementsCacheSize() instead
* @return statementsCacheSize
*/
@Deprecated
public int getPreparedStatementsCacheSize() {
logger.warn("Please use getStatementsCacheSize in place of getPreparedStatementsCacheSize. This method has been deprecated.");
return this.statementsCacheSize;
}
/** Deprecated. Use getStatementsCacheSize() instead
* @return statementsCacheSize
*/
@Deprecated
public int getPreparedStatementCacheSize() {
logger.warn("Please use getStatementsCacheSize in place of getPreparedStatementCacheSize. This method has been deprecated.");
return this.statementsCacheSize;
}
/**
* Deprecated. Use setStatementsCacheSize() instead.
*
* @param preparedStatementsCacheSize to set.
*/
@Deprecated
public void setPreparedStatementsCacheSize(int preparedStatementsCacheSize) {
logger.warn("Please use setStatementsCacheSize in place of setPreparedStatementsCacheSize. This method has been deprecated.");
this.statementsCacheSize = preparedStatementsCacheSize;
}
/**
* Sets statementsCacheSize setting.
*
* The number of statements to cache.
*
* @param statementsCacheSize to set.
*/
public void setStatementsCacheSize(int statementsCacheSize) {
this.statementsCacheSize = statementsCacheSize;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getStatementsCacheSize()
*/
public int getStatementsCacheSize() {
return this.statementsCacheSize;
}
/**
* Deprecated. Use set statementCacheSize instead.
*
* The number of statements to cache.
*
* @param statementsCacheSize to set.
*/
@Deprecated
public void setStatementCacheSize(int statementsCacheSize) {
logger.warn("Please use setStatementsCacheSize in place of setStatementCacheSize. This method has been deprecated.");
this.statementsCacheSize = statementsCacheSize;
}
/** Deprecated. Use getStatementsCacheSize instead
* @return no of cache size.
*/
@Deprecated
public int getStatementCacheSize() {
logger.warn("Please use getStatementsCacheSize in place of getStatementCacheSize. This method has been deprecated.");
return this.statementsCacheSize;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getReleaseHelperThreads()
*/
@Deprecated
public int getReleaseHelperThreads() {
return this.releaseHelperThreads;
}
/**
* Sets number of helper threads to create that will handle releasing a connection.
*
* When this value is set to zero, the application thread is blocked until the pool is able to perform all the necessary cleanup to
* recycle the connection and make it available for another thread.
*
* When a non-zero value is set, the pool will create threads that will take care of recycling a connection when it is closed (the
* application dumps the connection into a temporary queue to be processed asychronously to the application via the release helper
* threads).
*
* Useful when your application is doing lots of work on each connection
* (i.e. perform an SQL query, do lots of non-DB stuff and perform another query),
* otherwise will probably slow things down.
*
* @param releaseHelperThreads no to release
*/
@Deprecated
public void setReleaseHelperThreads(int releaseHelperThreads) {
logger.warn("releaseHelperThreads has been deprecated -- it tends to slow down your application more.");
this.releaseHelperThreads = releaseHelperThreads;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getStatementsCachedPerConnection()
*/
@Deprecated
public int getStatementsCachedPerConnection() {
return this.statementsCachedPerConnection;
}
/**
* DEPRECATED. No longer necessary to call at all.
*
* Sets no of statements cached per connection.
*
* The number of prepared statements to cache per connection. This is usually only useful if you attempt to
* prepare the same prepared statement string in the same connection (usually due to a wrong design condition).
*
* @param statementsCachedPerConnection to set
*/
@Deprecated
public void setStatementsCachedPerConnection(int statementsCachedPerConnection) {
this.statementsCachedPerConnection = statementsCachedPerConnection;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getConnectionHook()
*/
public ConnectionHook getConnectionHook() {
return this.connectionHook;
}
/** Sets the connection hook.
*
* Fully qualified class name that implements the ConnectionHook interface (or extends AbstractConnectionHook).
* BoneCP will callback the specified class according to the connection state (onAcquire, onCheckIn, onCheckout, onDestroy).
*
* @param connectionHook the connectionHook to set
*/
public void setConnectionHook(ConnectionHook connectionHook) {
this.connectionHook = connectionHook;
}
/** {@inheritDoc}
* @see com.jolbox.bonecp.BoneCPConfigMBean#getInitSQL()
*/
public String getInitSQL() {
return this.initSQL;
}
/** Specifies an initial SQL statement that is run only when a connection is first created.
* @param initSQL the initSQL to set
*/
public void setInitSQL(String initSQL) {
this.initSQL = checkNotNull(initSQL);
}
/** Returns if BoneCP is configured to create a helper thread to watch over connection acquires that are never released (or released
* twice).
* FOR DEBUG PURPOSES ONLY.
* @return the current closeConnectionWatch setting.
*/
public boolean isCloseConnectionWatch() {
return this.closeConnectionWatch;
}
/** Instruct the pool to create a helper thread to watch over connection acquires that are never released (or released twice).
* This is for debugging purposes only and will create a new thread for each call to getConnection().
* Enabling this option will have a big negative impact on pool performance.
* @param closeConnectionWatch set to true to enable thread monitoring.
*/
public void setCloseConnectionWatch(boolean closeConnectionWatch) {
this.closeConnectionWatch = closeConnectionWatch;
}
/** Returns true if SQL logging is currently enabled, false otherwise.
* @return the logStatementsEnabled status
*/
public boolean isLogStatementsEnabled() {
return this.logStatementsEnabled;
}
/** If enabled, log SQL statements being executed. This will also "fill in" the parameters
* for prepared statements so that you can see exactly what values where bound at the time
* you executed the statement.
* You will also need to set your log4j settings ("com.jolbox.bonecp") to DEBUG
* @param logStatementsEnabled the logStatementsEnabled to set
*/
public void setLogStatementsEnabled(boolean logStatementsEnabled) {
this.logStatementsEnabled = logStatementsEnabled;
}
/** Deprecated.
*
* @deprecated Use {@link #getAcquireRetryDelayInMs()} instead.
* @return the acquireRetryDelay
*/
@Deprecated
public long getAcquireRetryDelay() {
logger.warn("Please use getAcquireRetryDelayInMs in place of getAcquireRetryDelay. This method has been deprecated.");
return this.acquireRetryDelayInMs;
}
/** Deprecated.
* @param acquireRetryDelayInMs the acquireRetryDelay to set
* @deprecated Use {@link #setAcquireRetryDelayInMs(long)}.
*/
@Deprecated
public void setAcquireRetryDelay(int acquireRetryDelayInMs) {
logger.warn("Please use setAcquireRetryDelayInMs in place of setAcquireRetryDelay. This method has been deprecated.");
this.acquireRetryDelayInMs = acquireRetryDelayInMs;
}
/** Returns the number of ms to wait before attempting to obtain a connection again after a failure. Default: 7000.
* @return the acquireRetryDelay
*/
public long getAcquireRetryDelayInMs() {
return this.acquireRetryDelayInMs;
}
/**
* Returns the acquireRetryDelay setting with the specified granularity.
*
* @param timeUnit time granularity
* @return acquireRetryDelay
*/
public long getAcquireRetryDelay(TimeUnit timeUnit) {
return timeUnit.convert(this.acquireRetryDelayInMs, TimeUnit.MILLISECONDS);
}
/** Sets the number of ms to wait before attempting to obtain a connection again after a failure.
* @param acquireRetryDelay the acquireRetryDelay to set
*/
public void setAcquireRetryDelayInMs(long acquireRetryDelay) {
setAcquireRetryDelay(acquireRetryDelay, TimeUnit.MILLISECONDS);
}
/** Sets the number of ms to wait before attempting to obtain a connection again after a failure.
* @param acquireRetryDelay the acquireRetryDelay to set
* @param timeUnit time granularity
*/
public void setAcquireRetryDelay(long acquireRetryDelay, TimeUnit timeUnit) {
this.acquireRetryDelayInMs = TimeUnit.MILLISECONDS.convert(acquireRetryDelay, timeUnit);
}
/** Returns true if connection pool is to be initialized lazily.
* @return lazyInit setting
*/
public boolean isLazyInit() {
return this.lazyInit;
}
/** Set to true to force the connection pool to obtain the initial connections lazily.
* @param lazyInit the lazyInit setting to set
*/
public void setLazyInit(boolean lazyInit) {
this.lazyInit = lazyInit;
}
/** Returns true if the pool is configured to record all transaction activity and replay the transaction automatically in case
* of connection failures.
* @return the transactionRecoveryEnabled status
*/
public boolean isTransactionRecoveryEnabled() {
return this.transactionRecoveryEnabled;
}
/** Set to true to enable recording of all transaction activity and replay the transaction automatically in case
* of a connection failure.
* @param transactionRecoveryEnabled the transactionRecoveryEnabled status to set
*/
public void setTransactionRecoveryEnabled(boolean transactionRecoveryEnabled) {
this.transactionRecoveryEnabled = transactionRecoveryEnabled;
}
/** After attempting to acquire a connection and failing, try to connect these many times before giving up. Default 5.
* @return the acquireRetryAttempts value
*/
public int getAcquireRetryAttempts() {
return this.acquireRetryAttempts;
}
/** After attempting to acquire a connection and failing, try to connect these many times before giving up. Default 5.
* @param acquireRetryAttempts the acquireRetryAttempts to set
*/
public void setAcquireRetryAttempts(int acquireRetryAttempts) {
this.acquireRetryAttempts = acquireRetryAttempts;
}
/** Sets the connection hook class name. Consider using setConnectionHook() instead.
* @param connectionHookClassName the connectionHook class name to set
*/
public void setConnectionHookClassName(String connectionHookClassName) {
this.connectionHookClassName = checkNotNull(connectionHookClassName);
Object hookClass;
try {
hookClass = loadClass(connectionHookClassName).newInstance();
this.connectionHook = (ConnectionHook) hookClass;
} catch (Exception e) {
logger.error("Unable to create an instance of the connection hook class ("+connectionHookClassName+")");
this.connectionHook = null;
}
}
/** Returns the connection hook class name as passed via the setter
* @return the connectionHookClassName.
*/
public String getConnectionHookClassName() {
return this.connectionHookClassName;
}
/** Return true if JMX is disabled.
* @return the disableJMX.
*/
public boolean isDisableJMX() {
return this.disableJMX;
}
/** Set to true to disable JMX.
* @param disableJMX the disableJMX to set
*/
public void setDisableJMX(boolean disableJMX) {
this.disableJMX = disableJMX;
}
/** Returns the bean being used to return a connection.
* @return the datasourceBean that was set.
*/
public DataSource getDatasourceBean() {
return this.datasourceBean;
}
/** If set, use datasourceBean.getConnection() to obtain a new connection instead of Driver.getConnection().
* @param datasourceBean the datasourceBean to set
*/
public void setDatasourceBean(DataSource datasourceBean) {
this.datasourceBean = datasourceBean;
}
/** Deprecated.
* @deprecated Use {@link #getQueryExecuteTimeLimitInMs()} instead.
* @return the queryTimeLimit
*/
@Deprecated
public long getQueryExecuteTimeLimit() {
logger.warn("Please use getQueryExecuteTimeLimitInMs in place of getQueryExecuteTimeLimit. This method has been deprecated.");
return this.queryExecuteTimeLimitInMs;
}
/** Queries taking longer than this limit to execute are logged.
* @param queryExecuteTimeLimit the limit to set in milliseconds.
* @deprecated Use {@link #setQueryExecuteTimeLimitInMs(long)} instead.
*/
@Deprecated
public void setQueryExecuteTimeLimit(int queryExecuteTimeLimit) {
logger.warn("Please use setQueryExecuteTimeLimitInMs in place of setQueryExecuteTimeLimit. This method has been deprecated.");
setQueryExecuteTimeLimit(queryExecuteTimeLimit, TimeUnit.MILLISECONDS);
}
/** Return the query execute time limit.
* @return the queryTimeLimit
*/
public long getQueryExecuteTimeLimitInMs() {
return this.queryExecuteTimeLimitInMs;
}
/**
* Returns the queryExecuteTimeLimit setting with the specified granularity.
*
* @param timeUnit time granularity
* @return queryExecuteTimeLimit period
*/
public long getQueryExecuteTimeLimit(TimeUnit timeUnit) {
return timeUnit.convert(this.queryExecuteTimeLimitInMs, TimeUnit.MILLISECONDS);
}
/** Queries taking longer than this limit to execute are logged.
* @param queryExecuteTimeLimit the limit to set in milliseconds.
*/
public void setQueryExecuteTimeLimitInMs(long queryExecuteTimeLimit) {
setQueryExecuteTimeLimit(queryExecuteTimeLimit, TimeUnit.MILLISECONDS);
}
/** Queries taking longer than this limit to execute are logged.
* @param queryExecuteTimeLimit the limit to set in milliseconds.
* @param timeUnit
*/
public void setQueryExecuteTimeLimit(long queryExecuteTimeLimit, TimeUnit timeUnit) {
this.queryExecuteTimeLimitInMs = TimeUnit.MILLISECONDS.convert(queryExecuteTimeLimit, timeUnit);
}
/** Returns the pool watch connection threshold value.
* @return the poolAvailabilityThreshold currently set.
*/
public int getPoolAvailabilityThreshold() {
return this.poolAvailabilityThreshold;
}
/** Sets the Pool Watch thread threshold.
*
* The pool watch thread attempts to maintain a number of connections always available (between minConnections and maxConnections). This
* value sets the percentage value to maintain. For example, setting it to 20 means that if the following condition holds:
* Free Connections / MaxConnections < poolAvailabilityThreshold
*
* new connections will be created. In other words, it tries to keep at least 20% of the pool full of connections. Setting the value
* to zero will make the pool create new connections when it needs them but it also means your application may have to wait for new
* connections to be obtained at times.
*
* Default: 0.
*
* @param poolAvailabilityThreshold the poolAvailabilityThreshold to set
*/
public void setPoolAvailabilityThreshold(int poolAvailabilityThreshold) {
this.poolAvailabilityThreshold = poolAvailabilityThreshold;
}
/** Returns true if connection tracking has been disabled.
* @return the disableConnectionTracking
*/
public boolean isDisableConnectionTracking() {
return this.disableConnectionTracking;
}
/** If set to true, the pool will not monitor connections for proper closure. Enable this option if you only ever obtain
* your connections via a mechanism that is guaranteed to release the connection back to the pool (eg Spring's jdbcTemplate,
* some kind of transaction manager, etc).
*
* @param disableConnectionTracking set to true to disable. Default: false.
*/
public void setDisableConnectionTracking(boolean disableConnectionTracking) {
this.disableConnectionTracking = disableConnectionTracking;
}
/** Deprecated.
*
* @deprecated Use {@link #getConnectionTimeoutInMs()} instead.
* @return the connectionTimeout
*/
@Deprecated
public long getConnectionTimeout() {
logger.warn("Please use getConnectionTimeoutInMs in place of getConnectionTimeout. This method has been deprecated.");
return this.connectionTimeoutInMs;
}
/** Deprecated.
*
* @deprecated Use {@link #setConnectionTimeoutInMs(long)} instead.
* @param connectionTimeout the connectionTimeout to set
*/
@Deprecated
public void setConnectionTimeout(long connectionTimeout) {
logger.warn("Please use setConnectionTimeoutInMs in place of setConnectionTimeout. This method has been deprecated.");
this.connectionTimeoutInMs = connectionTimeout;
}
/** Returns the maximum time (in milliseconds) to wait before a call to getConnection is timed out.
* @return the connectionTimeout
*/
public long getConnectionTimeoutInMs() {
return this.connectionTimeoutInMs;
}
/**
* Returns the connectionTimeout with the specified granularity.
*
* @param timeUnit time granularity
* @return connectionTimeout period
*/
public long getConnectionTimeout(TimeUnit timeUnit) {
return timeUnit.convert(this.connectionTimeoutInMs, TimeUnit.MILLISECONDS);
}
/** Sets the maximum time (in milliseconds) to wait before a call to getConnection is timed out.
*
* Setting this to zero is similar to setting it to Long.MAX_VALUE
*
* Default: 0 ( = wait forever )
*
* @param connectionTimeoutinMs the connectionTimeout to set
*/
public void setConnectionTimeoutInMs(long connectionTimeoutinMs) {
setConnectionTimeout(connectionTimeoutinMs, TimeUnit.MILLISECONDS);
}
/** Sets the maximum time to wait before a call to getConnection is timed out.
*
* Setting this to zero is similar to setting it to Long.MAX_VALUE
*
* @param connectionTimeout
* @param timeUnit the unit of the connectionTimeout argument
*/
public void setConnectionTimeout(long connectionTimeout, TimeUnit timeUnit) {
this.connectionTimeoutInMs = TimeUnit.MILLISECONDS.convert(connectionTimeout, timeUnit);
}
/** Returns the currently configured driver properties.
* @return the driverProperties handle
*/
public Properties getDriverProperties() {
return this.driverProperties;
}
/** Sets properties that will be passed on to the driver.
*
* The properties handle should contain a list of arbitrary string tag/value pairs
* as connection arguments; normally at least a "user" and "password" property
* should be included. Failure to include the user or password properties will make the
* pool copy the values given in config.setUsername(..) and config.setPassword(..).
*
* Note that the pool will make a copy of these properties so as not to risk attempting to
* create a connection later on with different settings.
*
* @param driverProperties the driverProperties to set
*/
public void setDriverProperties(Properties driverProperties) {
// make a copy of the properties so that we don't attempt to create more connections
// later on and are possibly surprised by having different urls/usernames/etc
this.driverProperties = new Properties();
this.driverProperties.putAll(checkNotNull(driverProperties));
}
/** Deprecated.
* @deprecated Use {@link #getCloseConnectionWatchTimeoutInMs()} instead
* @return the watchTimeout currently set.
*/
@Deprecated
public long getCloseConnectionWatchTimeout() {
logger.warn("Please use getCloseConnectionWatchTimeoutInMs in place of getCloseConnectionWatchTimeout. This method has been deprecated.");
return this.closeConnectionWatchTimeoutInMs;
}
/** Deprecated.
* @param closeConnectionWatchTimeout the watchTimeout to set
* @deprecated Use {@link #setCloseConnectionWatchTimeoutInMs(long)} instead
*/
@Deprecated
public void setCloseConnectionWatchTimeout(long closeConnectionWatchTimeout) {
logger.warn("Please use setCloseConnectionWatchTimeoutInMs in place of setCloseConnectionWatchTimeout. This method has been deprecated.");
setCloseConnectionWatchTimeoutInMs(closeConnectionWatchTimeout);
}
/** Returns the no of ms to wait when close connection watch threads are enabled. 0 = wait forever.
* @return the watchTimeout currently set.
*/
public long getCloseConnectionWatchTimeoutInMs() {
return this.closeConnectionWatchTimeoutInMs;
}
/**
* Returns the closeConnectionWatchTimeout with the specified granularity.
*
* @param timeUnit time granularity
* @return closeConnectionWatchTimeout period
*/
public long getCloseConnectionWatchTimeout(TimeUnit timeUnit) {
return timeUnit.convert(this.closeConnectionWatchTimeoutInMs, TimeUnit.MILLISECONDS);
}
/** Sets the no of ms to wait when close connection watch threads are enabled. 0 = wait forever.
* @param closeConnectionWatchTimeout the watchTimeout to set
*/
public void setCloseConnectionWatchTimeoutInMs(long closeConnectionWatchTimeout) {
setCloseConnectionWatchTimeout(closeConnectionWatchTimeout, TimeUnit.MILLISECONDS);
}
/** Sets the time to wait when close connection watch threads are enabled. 0 = wait forever.
* @param closeConnectionWatchTimeout the watchTimeout to set
* @param timeUnit Time granularity
*/
public void setCloseConnectionWatchTimeout(long closeConnectionWatchTimeout, TimeUnit timeUnit) {
this.closeConnectionWatchTimeoutInMs = TimeUnit.MILLISECONDS.convert(closeConnectionWatchTimeout, timeUnit);
}
/**
* Returns the statementHelperThreads field. Deprecated.
* @return statementHelperThreads
*/
@Deprecated
public int getStatementReleaseHelperThreads() {
return this.statementReleaseHelperThreads;
}
/**
* Depracated method. Sets number of statement helper threads to create that will handle releasing a statement.
*
* When this value is set to zero, the application thread is blocked until the pool and JDBC driver are able to close off the statement.
*
* When a non-zero value is set, the pool will create threads that will take care of closing off the statement asychronously to the application via the release helper
* threads).
*
* Useful when your application is opening up lots of statements otherwise will probably slow things down.
*
* @param statementReleaseHelperThreads no to release
*/
@Deprecated
public void setStatementReleaseHelperThreads(int statementReleaseHelperThreads) {
logger.warn("statementReleaseHelperThreads has been deprecated -- it tends to slow down your application more.");
this.statementReleaseHelperThreads = statementReleaseHelperThreads;
}
/**
* Deprecated. Please use {@link #getMaxConnectionAgeInSeconds()} instead.
* @return maxConnectionAge
* @deprecated Please use {@link #getMaxConnectionAgeInSeconds()} instead.
*/
@Deprecated
public long getMaxConnectionAge() {
logger.warn("Please use getMaxConnectionAgeInSeconds in place of getMaxConnectionAge. This method has been deprecated.");
return this.maxConnectionAgeInSeconds;
}
/**
* Returns the maxConnectionAge field in seconds
* @return maxConnectionAge
*/
public long getMaxConnectionAgeInSeconds() {
return this.maxConnectionAgeInSeconds;
}
/**
* Returns the maxConnectionAge with the specified granularity.
*
* @param timeUnit time granularity
* @return maxConnectionAge period
*/
public long getMaxConnectionAge(TimeUnit timeUnit) {
return timeUnit.convert(this.maxConnectionAgeInSeconds, TimeUnit.SECONDS);
}
/**
* Deprecated. Use {{@link #setMaxConnectionAgeInSeconds(long)} instead.
*
* @param maxConnectionAgeInSeconds the maxConnectionAge to set
* @deprecated Please use {{@link #setMaxConnectionAgeInSeconds(long)} instead.
*/
@Deprecated
public void setMaxConnectionAge(long maxConnectionAgeInSeconds) {
logger.warn("Please use setmaxConnectionAgeInSecondsInSeconds in place of setMaxConnectionAge. This method has been deprecated.");
this.maxConnectionAgeInSeconds = maxConnectionAgeInSeconds;
}
/**
* Sets the maxConnectionAge in seconds. Any connections older than this setting will be closed
* off whether it is idle or not. Connections currently in use will not be affected until they
* are returned to the pool.
*
* @param maxConnectionAgeInSeconds the maxConnectionAge to set
*/
public void setMaxConnectionAgeInSeconds(long maxConnectionAgeInSeconds) {
setMaxConnectionAge(maxConnectionAgeInSeconds, TimeUnit.SECONDS);
}
/**
* Sets the maxConnectionAge. Any connections older than this setting will be closed
* off whether it is idle or not. Connections currently in use will not be affected until they
* are returned to the pool.
*
* @param maxConnectionAge the maxConnectionAge to set.
* @param timeUnit the unit of the maxConnectionAge argument.
*/
public void setMaxConnectionAge(long maxConnectionAge, TimeUnit timeUnit) {
this.maxConnectionAgeInSeconds = TimeUnit.SECONDS.convert(maxConnectionAge, timeUnit);
}
/**
* Returns the configFile field.
* @return configFile
*/
public String getConfigFile() {
return this.configFile;
}
/**
* Sets the configFile. If configured, this will cause the pool to initialise using the
* config file in the same way as if calling new BoneCPConfig(filename).
* @param configFile the configFile to set
*/
public void setConfigFile(String configFile) {
this.configFile = checkNotNull(configFile);
}
/**
* Returns the serviceOrder field.
* @return serviceOrder
*/
public String getServiceOrder() {
return this.serviceOrder;
}
/**
* Sets the queue serviceOrder. Values currently understood are FIFO and LIFO.
* @param serviceOrder the serviceOrder to set
*/
public void setServiceOrder(String serviceOrder) {
this.serviceOrder = checkNotNull(serviceOrder);
}
/**
* Returns the statisticsEnabled field.
* @return statisticsEnabled
*/
public boolean isStatisticsEnabled() {
return this.statisticsEnabled;
}
/**
* If set to true, keep track of some more statistics for exposure via JMX. Will slow down the pool
* operation.
* @param statisticsEnabled set to true to enable
*/
public void setStatisticsEnabled(boolean statisticsEnabled) {
this.statisticsEnabled = statisticsEnabled;
}
/**
* Returns the defaultAutoCommit field.
* @return defaultAutoCommit
*/
public boolean getDefaultAutoCommit() {
return this.defaultAutoCommit;
}
/**
* Sets the defaultAutoCommit setting for newly created connections. If not set, use driver default.
* @param defaultAutoCommit the defaultAutoCommit to set
*/
public void setDefaultAutoCommit(boolean defaultAutoCommit) {
this.defaultAutoCommit = defaultAutoCommit;
}
/**
* Returns the defaultReadOnly field.
* @return defaultReadOnly
*/
public Boolean getDefaultReadOnly() {
return this.defaultReadOnly;
}
/**
* Sets the defaultReadOnly setting for newly created connections. If not set, use driver default.
* @param defaultReadOnly the defaultReadOnly to set
*/
public void setDefaultReadOnly(Boolean defaultReadOnly) {
this.defaultReadOnly = checkNotNull(defaultReadOnly);
}
/**
* Returns the defaultCatalog field.
* @return defaultCatalog
*/
public String getDefaultCatalog() {
return this.defaultCatalog;
}
/**
* Sets the defaultCatalog setting for newly created connections. If not set, use driver default.
* @param defaultCatalog the defaultCatalog to set
*/
public void setDefaultCatalog(String defaultCatalog) {
this.defaultCatalog = checkNotNull(defaultCatalog);
}
/**
* Returns the defaultTransactionIsolation field.
* @return defaultTransactionIsolation
*/
public String getDefaultTransactionIsolation() {
return this.defaultTransactionIsolation;
}
/**
* Sets the defaultTransactionIsolation. Should be set to one of: NONE, READ_COMMITTED, READ_UNCOMMITTED,
* REPEATABLE_READ or SERIALIZABLE. If not set, will use driver default.
* @param defaultTransactionIsolation the defaultTransactionIsolation to set
*/
public void setDefaultTransactionIsolation(String defaultTransactionIsolation) {
this.defaultTransactionIsolation = checkNotNull(defaultTransactionIsolation);
}
/**
* Returns the defaultTransactionIsolationValue field.
* @return defaultTransactionIsolationValue
*/
protected int getDefaultTransactionIsolationValue() {
return this.defaultTransactionIsolationValue;
}
/**
* Sets the defaultTransactionIsolationValue.
* @param defaultTransactionIsolationValue the defaultTransactionIsolationValue to set
*/
protected void setDefaultTransactionIsolationValue(int defaultTransactionIsolationValue) {
this.defaultTransactionIsolationValue = defaultTransactionIsolationValue;
}
/**
* Default constructor. Attempts to fill settings in this order:
* 1. bonecp-default-config.xml file, usually found in the pool jar
* 2. bonecp-config.xml file, usually found in your application's classpath
* 3. Other hardcoded defaults in BoneCPConfig class.
*/
public BoneCPConfig(){
// try to load the default config file, if available from somewhere in the classpath
loadProperties("bonecp-default-config.xml");
// try to override with app specific config, if available
loadProperties("bonecp-config.xml");
}
/** Creates a new config using the given properties.
* @param props properties to set.
* @throws Exception on error
*/
public BoneCPConfig(Properties props) throws Exception {
this();
this.setProperties(checkNotNull(props));
}
/** Initialize the configuration by loading bonecp-config.xml containing the settings.
* @param sectionName section to load
* @throws Exception on parse errors
*/
public BoneCPConfig(String sectionName) throws Exception{
this(BoneCPConfig.class.getResourceAsStream("/bonecp-config.xml"), checkNotNull(sectionName));
}
/** Initialise the configuration by loading an XML file containing the settings.
* @param xmlConfigFile file to load
* @param sectionName section to load
* @throws Exception
*/
public BoneCPConfig(InputStream xmlConfigFile, String sectionName) throws Exception{
this();
setXMLProperties(xmlConfigFile, checkNotNull(sectionName));
}
/**
* @param xmlConfigFile
* @param sectionName
* @throws Exception
*/
private void setXMLProperties(InputStream xmlConfigFile, String sectionName)
throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
// ugly XML parsing, but this is built-in the JDK.
try {
db = dbf.newDocumentBuilder();
Document doc = db.parse(xmlConfigFile);
doc.getDocumentElement().normalize();
// get the default settings
Properties settings = parseXML(doc, null);
if (sectionName != null){
// override with custom settings
settings.putAll(parseXML(doc, sectionName));
}
// set the properties
setProperties(settings);
} catch (Exception e) {
throw e;
} finally {
if (xmlConfigFile != null){ // safety
xmlConfigFile.close();
}
}
}
/** Lowercases the first character.
* @param name
* @return the same string with the first letter in lowercase
*/
private String lowerFirst(String name) {
return name.substring(0, 1).toLowerCase()+name.substring(1);
}
/**
* Sets the properties by reading off entries in the given parameter (where each key is equivalent to the field name)
* @param props Parameter list to set
* @throws Exception on error
*/
public void setProperties(Properties props) throws Exception {
// Use reflection to read in all possible properties of int, String or boolean.
for (Method method: BoneCPConfig.class.getDeclaredMethods()){
String tmp = null;
if (method.getName().startsWith("is")){
tmp = lowerFirst(method.getName().substring(2));
} else if (method.getName().startsWith("set")){
tmp = lowerFirst(method.getName().substring(3));
} else {
continue;
}
if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(int.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
try{
method.invoke(this, Integer.parseInt(val));
} catch (NumberFormatException e){
// do nothing, use the default value
}
}
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(long.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
try{
method.invoke(this, Long.parseLong(val));
} catch (NumberFormatException e){
// do nothing, use the default value
}
}
} else if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(String.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
method.invoke(this, val);
}
} if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0].equals(boolean.class)){
String val = props.getProperty(tmp);
if (val == null){
val = props.getProperty("bonecp."+tmp); // hibernate provider style
}
if (val != null) {
method.invoke(this, Boolean.parseBoolean(val));
}
}
}
}
/** Parses the given XML doc to extract the properties and return them into a java.util.Properties.
* @param doc to parse
* @param sectionName which section to extract
* @return Properties map
*/
private Properties parseXML(Document doc, String sectionName) {
int found = -1;
Properties results = new Properties();
NodeList config = null;
if (sectionName == null){
config = doc.getElementsByTagName("default-config");
found = 0;
} else {
config = doc.getElementsByTagName("named-config");
if(config != null && config.getLength() > 0) {
for (int i = 0; i < config.getLength(); i++) {
Node node = config.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE ){
NamedNodeMap attributes = node.getAttributes();
if (attributes != null && attributes.getLength() > 0){
Node name = attributes.getNamedItem("name");
if (name.getNodeValue().equalsIgnoreCase(sectionName)){
found = i;
break;
}
}
}
}
}
if (found == -1){
config = null;
logger.warn("Did not find "+sectionName+" section in config file. Reverting to defaults.");
}
}
if(config != null && config.getLength() > 0) {
Node node = config.item(found);
if(node.getNodeType() == Node.ELEMENT_NODE){
Element elementEntry = (Element)node;
NodeList childNodeList = elementEntry.getChildNodes();
for (int j = 0; j < childNodeList.getLength(); j++) {
Node node_j = childNodeList.item(j);
if (node_j.getNodeType() == Node.ELEMENT_NODE) {
Element piece = (Element) node_j;
NamedNodeMap attributes = piece.getAttributes();
if (attributes != null && attributes.getLength() > 0){
results.put(attributes.item(0).getNodeValue(), piece.getTextContent());
}
}
}
}
}
return results;
}
/**
* Returns the current externalAuth setting.
* @return externalAuth setting
*/
public boolean isExternalAuth() {
return this.externalAuth;
}
/**
* If set to true, no attempts at passing in a username/password will be attempted
* when trying to obtain a raw (driver) connection. Useful for cases when you already have
* another mechanism on authentication eg NTLM.
*
* @param externalAuth True to enable external auth.
*/
public void setExternalAuth(boolean externalAuth) {
this.externalAuth = externalAuth;
}
/**
* Performs validation on the config object.
*
*/
public void sanitize(){
if (this.configFile != null){
loadProperties(this.configFile);
}
if (this.poolStrategy == null || !(this.poolStrategy.equalsIgnoreCase("DEFAULT") || this.poolStrategy.equalsIgnoreCase("CACHED"))){
logger.warn("Unrecognised pool strategy. Allowed values are DEFAULT and CACHED. Setting to DEFAULT.");
this.poolStrategy = "DEFAULT";
}
this.poolStrategy = this.poolStrategy.toUpperCase();
if ((this.poolAvailabilityThreshold < 0) || (this.poolAvailabilityThreshold > 100)){
this.poolAvailabilityThreshold = 20;
}
if (this.defaultTransactionIsolation != null){
this.defaultTransactionIsolation = this.defaultTransactionIsolation.trim().toUpperCase();
if (this.defaultTransactionIsolation.equals("NONE")){
this.defaultTransactionIsolationValue = Connection.TRANSACTION_NONE;
} else if (this.defaultTransactionIsolation.equals("READ_COMMITTED") || this.defaultTransactionIsolation.equals("READ COMMITTED")){
this.defaultTransactionIsolationValue = Connection.TRANSACTION_READ_COMMITTED;
} else if (this.defaultTransactionIsolation.equals("REPEATABLE_READ") || this.defaultTransactionIsolation.equals("REPEATABLE READ")){
this.defaultTransactionIsolationValue = Connection.TRANSACTION_REPEATABLE_READ;
} else if (this.defaultTransactionIsolation.equals("READ_UNCOMMITTED") || this.defaultTransactionIsolation.equals("READ UNCOMMITTED")){
this.defaultTransactionIsolationValue = Connection.TRANSACTION_READ_UNCOMMITTED;
} else if (this.defaultTransactionIsolation.equals("SERIALIZABLE")){
this.defaultTransactionIsolationValue = Connection.TRANSACTION_SERIALIZABLE;
} else {
logger.warn("Unrecognized defaultTransactionIsolation value. Using driver default.");
this.defaultTransactionIsolationValue = -1;
}
}
if (this.maxConnectionsPerPartition < 1) {
logger.warn("Max Connections < 1. Setting to 20");
this.maxConnectionsPerPartition = 20;
}
if (this.minConnectionsPerPartition < 0) {
logger.warn("Min Connections < 0. Setting to 1");
this.minConnectionsPerPartition = 1;
}
if (this.minConnectionsPerPartition > this.maxConnectionsPerPartition) {
logger.warn("Min Connections > max connections");
this.minConnectionsPerPartition = this.maxConnectionsPerPartition;
}
if (this.acquireIncrement <= 0) {
logger.warn("acquireIncrement <= 0. Setting to 1.");
this.acquireIncrement = 1;
}
if (this.partitionCount < 1) {
logger.warn("partitions < 1! Setting to 1");
this.partitionCount = 1;
}
if (this.releaseHelperThreads < 0){
logger.warn("releaseHelperThreads < 0! Setting to 0");
this.releaseHelperThreads = 0;
}
if (this.statementReleaseHelperThreads < 0){
logger.warn("statementReleaseHelperThreads < 0! Setting to 0");
this.statementReleaseHelperThreads = 0;
}
if (this.statementsCacheSize < 0) {
logger.warn("preparedStatementsCacheSize < 0! Setting to 0");
this.statementsCacheSize = 0;
}
if (this.acquireRetryDelayInMs <= 0) {
this.acquireRetryDelayInMs = 1000;
}
if (!this.externalAuth &&
(this.datasourceBean == null) &&
this.driverProperties == null
&& (this.jdbcUrl == null || this.jdbcUrl.trim().equals(""))){
logger.warn("JDBC url was not set in config!");
}
if (!this.externalAuth && this.datasourceBean == null && this.driverProperties == null &&
(this.username == null || this.username.trim().equals(""))){
logger.warn("JDBC username was not set in config!");
}
if (!this.externalAuth && (this.datasourceBean == null) && (this.driverProperties == null) && (this.password == null)){
logger.warn("JDBC password was not set in config!");
}
// if not external auth and no datasource and we have driver properties set...
if (!this.externalAuth && this.datasourceBean == null && this.driverProperties != null){
if ((this.driverProperties.get(USER) == null) && this.username == null){
logger.warn("JDBC username not set in driver properties and not set in pool config either");
} else if ((this.driverProperties.get(USER) == null) && this.username != null){
logger.warn("JDBC username not set in driver properties, copying it from pool config");
this.driverProperties.setProperty(USER, this.username);
} else if (this.username != null && !this.driverProperties.get(USER).equals(this.username)){
logger.warn("JDBC username set in driver properties does not match the one set in the pool config. Overriding it with pool config.");
this.driverProperties.setProperty(USER, this.username);
}
}
// if no datasource and we have driver properties set...
if (!this.externalAuth && this.datasourceBean == null && this.driverProperties != null){
if ((this.driverProperties.get(PASSWORD) == null) && this.password == null){
logger.warn("JDBC password not set in driver properties and not set in pool config either");
} else if ((this.driverProperties.get(PASSWORD) == null) && this.password != null){
logger.warn("JDBC password not set in driver properties, copying it from pool config");
this.driverProperties.setProperty(PASSWORD, this.password);
} else if (this.password != null && !this.driverProperties.get(PASSWORD).equals(this.password)){
logger.warn("JDBC password set in driver properties does not match the one set in the pool config. Overriding it with pool config.");
this.driverProperties.setProperty(PASSWORD, this.password);
}
// maintain sanity between the two states
this.username = this.driverProperties.getProperty(USER);
this.password = this.driverProperties.getProperty(PASSWORD);
}
if (this.username != null){
this.username = this.username.trim();
}
if (this.jdbcUrl != null){
this.jdbcUrl = this.jdbcUrl.trim();
}
if (this.password != null){
this.password = this.password.trim();
}
if (this.connectionTestStatement != null) {
this.connectionTestStatement = this.connectionTestStatement.trim();
}
this.serviceOrder = this.serviceOrder.toUpperCase();
if (!(this.serviceOrder.equals("FIFO") || this.serviceOrder.equals("LIFO"))){
logger.warn("Queue service order is not set to FIFO or LIFO. Defaulting to FIFO.");
this.serviceOrder = "FIFO";
}
if (this.logStatementsEnabled && !logger.isDebugEnabled()){
logger.warn("LogStatementsEnabled is set to true, but log4j level is not set at DEBUG. Disabling statement logging.");
logStatementsEnabled = false;
}
}
/**
* Loads the given properties file using the classloader.
* @param filename Config filename to load
*
*/
protected void loadProperties(String filename) {
ClassLoader thclassLoader = Thread.currentThread().getContextClassLoader();
if (thclassLoader != null){
URL url = thclassLoader.getResource(filename);
if (url != null){
try {
this.setXMLProperties(url.openStream(), null);
} catch (Exception e) {
// do nothing
}
}
}
}
@Override
public String toString() {
String result = null;
if (this.datasourceBean != null){
result = String.format(CONFIG_DS_TOSTRING, this.partitionCount, this.maxConnectionsPerPartition, this.minConnectionsPerPartition,
getIdleMaxAgeInMinutes(),
getIdleConnectionTestPeriodInMinutes(), this.poolStrategy);
} else {
result = String.format(CONFIG_TOSTRING, this.jdbcUrl,
this.username, this.partitionCount, this.maxConnectionsPerPartition, this.minConnectionsPerPartition,
getIdleMaxAgeInMinutes(),
getIdleConnectionTestPeriodInMinutes(), this.poolStrategy);
}
return result;
}
/** Loads the given class, respecting the given classloader.
* @param clazz class to load
* @return Loaded class
* @throws ClassNotFoundException
*/
protected Class<?> loadClass(String clazz) throws ClassNotFoundException {
if (this.classLoader == null){
return Class.forName(clazz);
}
return Class.forName(clazz, true, this.classLoader);
}
/** Returns the currently active classloader.
* @return the classLoader
*/
public ClassLoader getClassLoader() {
return this.classLoader;
}
/** Sets the classloader to use to load JDBC driver and hooks (set to null to use default).
* @param classLoader the classLoader to set
*/
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public BoneCPConfig clone() throws CloneNotSupportedException {
BoneCPConfig clone = (BoneCPConfig)super.clone();
Field[] fields = this.getClass().getDeclaredFields();
for (Field field: fields){
try {
field.set(clone, field.get(this));
} catch (Exception e) {
// should never happen
}
}
return clone;
}
/** Returns true if this instance has the same config as a given config.
* @param that
* @return true if the instance has the same config, false otherwise.
*/
public boolean hasSameConfiguration(BoneCPConfig that){
if ( that != null && Objects.equal(this.acquireIncrement, that.getAcquireIncrement())
&& Objects.equal(this.acquireRetryDelayInMs, that.getAcquireRetryDelayInMs())
&& Objects.equal(this.closeConnectionWatch, that.isCloseConnectionWatch())
&& Objects.equal(this.logStatementsEnabled, that.isLogStatementsEnabled())
&& Objects.equal(this.connectionHook, that.getConnectionHook())
&& Objects.equal(this.connectionTestStatement, that.getConnectionTestStatement())
&& Objects.equal(this.idleConnectionTestPeriodInSeconds, that.getIdleConnectionTestPeriod(TimeUnit.SECONDS))
&& Objects.equal(this.idleMaxAgeInSeconds, that.getIdleMaxAge(TimeUnit.SECONDS))
&& Objects.equal(this.initSQL, that.getInitSQL())
&& Objects.equal(this.jdbcUrl, that.getJdbcUrl())
&& Objects.equal(this.maxConnectionsPerPartition, that.getMaxConnectionsPerPartition())
&& Objects.equal(this.minConnectionsPerPartition, that.getMinConnectionsPerPartition())
&& Objects.equal(this.partitionCount, that.getPartitionCount())
&& Objects.equal(this.releaseHelperThreads, that.getReleaseHelperThreads())
&& Objects.equal(this.statementsCacheSize, that.getStatementsCacheSize())
&& Objects.equal(this.username, that.getUsername())
&& Objects.equal(this.password, that.getPassword())
&& Objects.equal(this.lazyInit, that.isLazyInit())
&& Objects.equal(this.transactionRecoveryEnabled, that.isTransactionRecoveryEnabled())
&& Objects.equal(this.acquireRetryAttempts, that.getAcquireRetryAttempts())
&& Objects.equal(this.statementReleaseHelperThreads, that.getStatementReleaseHelperThreads())
&& Objects.equal(this.closeConnectionWatchTimeoutInMs, that.getCloseConnectionWatchTimeout())
&& Objects.equal(this.connectionTimeoutInMs, that.getConnectionTimeoutInMs())
&& Objects.equal(this.datasourceBean, that.getDatasourceBean())
&& Objects.equal(this.getQueryExecuteTimeLimitInMs(), that.getQueryExecuteTimeLimitInMs())
&& Objects.equal(this.poolAvailabilityThreshold, that.getPoolAvailabilityThreshold())
&& Objects.equal(this.poolName, that.getPoolName())
&& Objects.equal(this.disableConnectionTracking, that.isDisableConnectionTracking())
){
return true;
}
return false;
}
/**
* Returns the deregisterDriverOnClose setting.
* @return deregisterDriverOnClose
*/
public boolean isDeregisterDriverOnClose() {
return this.deregisterDriverOnClose;
}
/**
* If set to true, try to unregister the JDBC driver when pool is shutdown.
* @param deregisterDriverOnClose the deregisterDriverOnClose setting.
*/
public void setDeregisterDriverOnClose(boolean deregisterDriverOnClose) {
this.deregisterDriverOnClose = deregisterDriverOnClose;
}
/**
* Returns the nullOnConnectionTimeout field.
* @return nullOnConnectionTimeout
*/
public boolean isNullOnConnectionTimeout() {
return this.nullOnConnectionTimeout;
}
/**
* Sets the nullOnConnectionTimeout.
*
* If true, return null on connection timeout rather than throw an exception. This performs
* better but must be handled differently in your application. This only
* makes sense when using the connectionTimeout config option.
* @param nullOnConnectionTimeout the nullOnConnectionTimeout to set
*/
public void setNullOnConnectionTimeout(boolean nullOnConnectionTimeout) {
this.nullOnConnectionTimeout = nullOnConnectionTimeout;
}
/**
* Returns the resetConnectionOnClose setting.
* @return resetConnectionOnClose
*/
public boolean isResetConnectionOnClose() {
return this.resetConnectionOnClose;
}
/**
* If true, issue a reset (rollback) on connection close in case client forgot it.
* @param resetConnectionOnClose the resetConnectionOnClose to set
*/
public void setResetConnectionOnClose(boolean resetConnectionOnClose) {
this.resetConnectionOnClose = resetConnectionOnClose;
}
/**
* Returns the detectUnresolvedTransactions field.
* @return detectUnresolvedTransactions
*/
public boolean isDetectUnresolvedTransactions() {
return this.detectUnresolvedTransactions;
}
/**
* If true, and resetConnectionOnClose is also true, the pool will print out a stack
* trace of the location where you had a connection that specified setAutoCommit(false)
* but then forgot to call commit/rollback before closing it off. This feature is intended
* for debugging only.
* @param detectUnresolvedTransactions the detectUnresolvedTransactions to set
*/
public void setDetectUnresolvedTransactions(boolean detectUnresolvedTransactions) {
this.detectUnresolvedTransactions = detectUnresolvedTransactions;
}
/**
* Returns the poolStrategy field.
* @return poolStrategy
*/
public String getPoolStrategy() {
return this.poolStrategy;
}
/**
* Sets the poolStrategy. Currently supported strategies are DEFAULT and CACHED. This is an experimental feature!
*
* DEFAULT strategy operates in a manner that has been used in the pool since the very first
* version: it tries to obtain a connection from a queue.
*
* CACHED stores each connection in a thread-local variable so that next time the same thread
* asks for a connection, it gets the same one assigned to it (if it asks for more than one, it
* will be allocated a new one). This is very fast but you must ensure that the number of threads
* asking for a connection is less than or equal to the number of connections you have made
* available. Should you exceed this limit, the pool will switch back (permanently) to the DEFAULT
* strategy which will cause a one-time performance hit. Use this strategy if your threads are
* managed eg in a Tomcat environment where you can limit the number of threads that it can
* handle. A typical use case would be a web service that always requires some form of database access,
* therefore a service would have little point in accepting a new incoming socket connection if it
* still has to wait in order to obtain a connection.
*
* Essentially this means that you are pushing back the lock down to the socket or thread layer.
* While the first few thread hits will be slower than in the DEFAULT strategy, significant performance
* gains are to be expected as the thread gets increasingly re-used (i.e. initially you should expect
* the first few rounds to be measurably slower than the DEFAULT strategy but once the caches
* get more hits you should get >2x better performance).
*
* Threads that are killed off are detected during the next garbage collection and result in
* their allocated connections from being taken back though since GC timing is not guaranteed
* you should ideally set your minimum pool size to be equal to the maximum pool size.
*
* Therefore for best results, make sure that the configured minConnectionPerPartition = maxConnectionPerPartition = min Threads = max Threads.
*
*
* @param poolStrategy the poolStrategy to set
*/
public void setPoolStrategy(String poolStrategy) {
this.poolStrategy = poolStrategy;
}
/**
* Returns the closeOpenStatements field.
* @return closeOpenStatements
*/
public boolean isCloseOpenStatements() {
return this.closeOpenStatements;
}
/**
* If true, track statements and close them if application forgot to do so. See also:
* {@link BoneCPConfig#detectUnclosedStatements}. Do not set if your connections are managed
* eg via Spring jdbcTemplate or hibernate since those frameworks will always automatically
* close off your statements. This option has a negative performance hit.
*
* @param closeOpenStatements the closeOpenStatements to set
*/
public void setCloseOpenStatements(boolean closeOpenStatements) {
this.closeOpenStatements = closeOpenStatements;
}
/**
* Returns the detectUnclosedStatements field.
* @return detectUnclosedStatements
*/
public boolean isDetectUnclosedStatements() {
return this.detectUnclosedStatements;
}
/**
* Sets the detectUnclosedStatements. If true, print out a stack trace of where a statement was opened but not closed before
* the connection was closed. See also: {@link BoneCPConfig#closeOpenStatements}.
* @param detectUnclosedStatements the detectUnclosedStatements to set
*/
public void setDetectUnclosedStatements(boolean detectUnclosedStatements) {
this.detectUnclosedStatements = detectUnclosedStatements;
}
/** If set, pool will call this for every new connection that's created. JDK 1.6 and higher only.
* @param properties Properties to set.
*
*/
public void setClientInfo(Properties properties) {
this.clientInfo = properties;
}
/**
* Returns the clientInfo field.
* @return clientInfo
*/
public Properties getClientInfo() {
return this.clientInfo;
}
}