Merge branch 'dev'

* dev: (77 commits)
  Unwrap DataSource test.
  Discourage users from turning on metrics until they do something useful.
  Add idle fill test.
  Reduce the number of filler threads ... 8 threads is too many on a hyper threaded 4-core CPU.
  Start implementing [optional] metrics for HikariCP.
  Additional connection tests.
  Add provided dependency on coda hale metrics.
  Merged #48 fix connectionTest validation
  Change defaults.
  Default minIdle the same as maxPoolSize.
  Split out stateless utility functions to reduce the weight of the HikariPool class (increases readability).
  Rename idleConnectionBag to connectionBag.
  Change connectionTimeout minimum to 250ms.
  Fix timing edge case in test.
  Check validation error
  Improve warnings for invalid configuration
  Use the DriverManager to create connections.
  Update changes.log.
  Update changes.log.
  Remove deprecated calls from tests, fix test conditions for new background fill behavior.
  ...
pull/60/head
Brett Wooldridge 11 years ago
commit 5c1592cdb4

@ -1,5 +1,40 @@
HikariCP Changes
Changes between 1.3.3 and 1.3.4
*) Added new property isolateInternalQueries used to control whether
internal pool queries such as connection alive tests are isolated
in their own transaction.
*) Added properties for DriverManager (driverClassName) and JDBC URL-based
(jdbcUrl) configuration. 1999 called and wants its JDBC driver back.
*) Added new username and password properties to allow default authentication
for connections.
*) Added support for the getConnection(username, password) method signature
to HikariDataSource.
*) Added new property readOnly to control the default read-only status
of connections in the pool.
*) Deprecated acquireIncrement property.
*) Deprecated acquireRetries property.
*) Deprecated acquireRetryDelay property.
*) Deprecated minimumPoolSize property.
*) Added new property minimumIdle used to control the minimum number of
idle connections the pool should try to maintain on a running basis.
*) Added evictConnection(Connection) method to HikariDataSource to allow
special cases where users wish to forcibly eject a connection from
the pool. To use used cautiously, read the JavaDoc before using.
*) Various bug fixes and minor enhancements.
Changes between 1.3.2 and 1.3.3
*) Removed shared state contention that was causing excessive CPU cache-line

@ -83,6 +83,12 @@
<version>4.3.0.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
<!-- OSGi test -->
<dependency>
@ -148,7 +154,7 @@
<Bundle-Name>HikariCP</Bundle-Name>
<Export-Package>com.zaxxer.hikari</Export-Package>
<Import-Package>
com.sun.tools.attach,javassist.*,
javassist.*,
javax.management,
javax.sql,
javax.sql.rowset,
@ -199,7 +205,7 @@
<version>2.9.1</version>
<configuration>
<show>public</show>
<excludePackageNames>com.zaxxer.hikari.*</excludePackageNames>
<!-- excludePackageNames>com.zaxxer.hikari.*</excludePackageNames -->
<attach>true</attach>
<maxmemory>1024m</maxmemory>
</configuration>

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Brett Wooldridge
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,7 +37,7 @@ public class HikariConfig implements HikariConfigMBean
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariConfig.class);
private static final long CONNECTION_TIMEOUT = 5000L;
private static final long CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private static final long IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10);
private static final long MAX_LIFETIME = TimeUnit.MINUTES.toMillis(30);
@ -45,26 +45,32 @@ public class HikariConfig implements HikariConfigMBean
// Properties changeable at runtime through the MBean
//
private volatile int acquireRetries;
private volatile long connectionTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minPoolSize;
private volatile int minIdle;
// Properties NOT changeable at runtime
//
private String transactionIsolationName;
private String catalog;
private String connectionCustomizerClassName;
private String connectionInitSql;
private String connectionTestQuery;
private String dataSourceClassName;
private String catalog;
private String driverClassName;
private String jdbcUrl;
private String password;
private String poolName;
private String transactionIsolationName;
private String username;
private boolean isAutoCommit;
private boolean isReadOnly;
private boolean isInitializationFailFast;
private boolean isJdbc4connectionTest;
private boolean isIsolateInternalQueries;
private boolean isRecordMetrics;
private boolean isRegisterMbeans;
private DataSource dataSource;
private Properties dataSourceProperties;
@ -83,15 +89,15 @@ public class HikariConfig implements HikariConfigMBean
{
dataSourceProperties = new Properties();
acquireRetries = 3;
connectionTimeout = CONNECTION_TIMEOUT;
idleTimeout = IDLE_TIMEOUT;
isAutoCommit = true;
isJdbc4connectionTest = true;
minPoolSize = 10;
maxPoolSize = 60;
minIdle = -1;
maxPoolSize = 10;
maxLifetime = MAX_LIFETIME;
poolName = "HikariPool-" + poolNumber++;
isRecordMetrics = false;
transactionIsolation = -1;
}
@ -136,89 +142,122 @@ public class HikariConfig implements HikariConfigMBean
}
}
public int getAcquireIncrement()
{
return 0;
}
@Deprecated
public void setAcquireIncrement(int acquireIncrement)
{
LOGGER.warn("The acquireIncrement property has been retired, remove it from your pool configuration to avoid this warning.");
}
/** {@inheritDoc} */
public int getAcquireRetries()
{
return acquireRetries;
}
/** {@inheritDoc} */
@Deprecated
public void setAcquireRetries(int acquireRetries)
{
if (acquireRetries < 0)
{
throw new IllegalArgumentException("acquireRetries cannot be negative");
}
this.acquireRetries = acquireRetries;
}
public long getAcquireRetryDelay()
{
return 0;
LOGGER.warn("The acquireRetries property has been retired, remove it from your pool configuration to avoid this warning.");
}
@Deprecated
public void setAcquireRetryDelay(long acquireRetryDelayMs)
{
LOGGER.warn("The acquireRetryDelay property has been retired, remove it from your pool configuration to avoid this warning.");
}
/**
* Get the default catalog name to be set on connections.
*
* @return the default catalog name
*/
public String getCatalog()
{
return catalog;
}
/**
* Set the default catalog name to be set on connections.
*
* @param catalog the catalog name, or null
*/
public void setCatalog(String catalog)
{
this.catalog = catalog;
}
/**
* Get the name of the connection customizer class to instantiate and execute
* on all new connections.
*
* @return the name of the customizer class, or null
*/
public String getConnectionCustomizerClassName()
{
return connectionCustomizerClassName;
}
/**
* Set the name of the connection customizer class to instantiate and execute
* on all new connections.
*
* @param connectionCustomizerClassName the name of the customizer class
*/
public void setConnectionCustomizerClassName(String connectionCustomizerClassName)
{
this.connectionCustomizerClassName = connectionCustomizerClassName;
}
/**
* Get the SQL query to be executed to test the validity of connections.
*
* @return the SQL query string, or null
*/
public String getConnectionTestQuery()
{
return connectionTestQuery;
}
/**
* Set the SQL query to be executed to test the validity of connections. Using
* the JDBC4 {@link Connection.isValid()} method to test connection validity can
* be more efficient on some databases and is recommended. See
* {@link HikariConfig#setJdbc4ConnectionTest(boolean)}.
*
* @param connectionTestQuery a SQL query string
*/
public void setConnectionTestQuery(String connectionTestQuery)
{
this.connectionTestQuery = connectionTestQuery;
}
/**
* Get the SQL string that will be executed on all new connections when they are
* created, before they are added to the pool.
*
* @return the SQL to execute on new connections, or null
*/
public String getConnectionInitSql()
{
return connectionInitSql;
}
/**
* Set the SQL string that will be executed on all new connections when they are
* created, before they are added to the pool. If this query fails, it will be
* treated as a failed connection attempt.
*
* @param connectionInitSql the SQL to execute on new connections
*/
public void setConnectionInitSql(String connectionInitSql)
{
this.connectionInitSql = connectionInitSql;
}
/** {@inheritDoc} */
@Override
public long getConnectionTimeout()
{
return connectionTimeout;
}
/** {@inheritDoc} */
@Override
public void setConnectionTimeout(long connectionTimeoutMs)
{
if (connectionTimeoutMs == 0)
@ -235,11 +274,23 @@ public class HikariConfig implements HikariConfigMBean
}
}
/**
* Get the {@link DataSource} that has been explicitly specified to be wrapped by the
* pool.
*
* @return the {@link DataSource} instance, or null
*/
public DataSource getDataSource()
{
return dataSource;
}
/**
* Set a {@link DataSource} for the pool to explicitly wrap. This setter is not
* available through property file based initialization.
*
* @param dataSource a specific {@link DataSource} to be wrapped by the pool
*/
public void setDataSource(DataSource dataSource)
{
this.dataSource = dataSource;
@ -270,38 +321,96 @@ public class HikariConfig implements HikariConfigMBean
dataSourceProperties.putAll(dsProperties);
}
public void setDriverClassName(String driverClassName)
{
try
{
Class<?> driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
driverClass.newInstance();
this.driverClassName = driverClassName;
}
catch (Exception e)
{
throw new RuntimeException("driverClassName specified class '" + driverClassName + "' could not be loaded", e);
}
}
/** {@inheritDoc} */
@Override
public long getIdleTimeout()
{
return idleTimeout;
}
/** {@inheritDoc} */
@Override
public void setIdleTimeout(long idleTimeoutMs)
{
this.idleTimeout = idleTimeoutMs;
}
public String getJdbcUrl()
{
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl)
{
this.jdbcUrl = jdbcUrl;
}
/**
* Get the default auto-commit behavior of connections in the pool.
*
* @return the default auto-commit behavior of connections
*/
public boolean isAutoCommit()
{
return isAutoCommit;
}
/**
* Set the default auto-commit behavior of connections in the pool.
*
* @param isAutoCommit the desired auto-commit default for connections
*/
public void setAutoCommit(boolean isAutoCommit)
{
this.isAutoCommit = isAutoCommit;
}
/**
* Get whether or not the construction of the pool should throw an exception
* if the minimum number of connections cannot be created.
*
* @return whether or not initialization should fail on error immediately
*/
public boolean isInitializationFailFast()
{
return isInitializationFailFast;
}
/**
* Set whether or not the construction of the pool should throw an exception
* if the minimum number of connections cannot be created.
*
* @param failFast true if the pool should fail if the minimum connections cannot be created
*/
public void setInitializationFailFast(boolean failFast)
{
isInitializationFailFast = failFast;
}
public boolean isIsolateInternalQueries()
{
return isIsolateInternalQueries;
}
public void setIsolateInternalQueries(boolean isolate)
{
this.isIsolateInternalQueries = isolate;
}
public boolean isJdbc4ConnectionTest()
{
return isJdbc4connectionTest;
@ -312,6 +421,31 @@ public class HikariConfig implements HikariConfigMBean
this.isJdbc4connectionTest = useIsValid;
}
public boolean isReadOnly()
{
return isReadOnly;
}
public void setReadOnly(boolean readOnly)
{
this.isReadOnly = readOnly;
}
public boolean isRecordMetrics()
{
return isRecordMetrics;
}
/**
* Currently not supported.
* @param recordMetrics
*/
@Deprecated
public void setRecordMetrics(boolean recordMetrics)
{
this.isRecordMetrics = recordMetrics;
}
public boolean isRegisterMbeans()
{
return isRegisterMbeans;
@ -323,57 +457,54 @@ public class HikariConfig implements HikariConfigMBean
}
/** {@inheritDoc} */
@Override
public long getLeakDetectionThreshold()
{
return leakDetectionThreshold;
}
/** {@inheritDoc} */
@Override
public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
{
this.leakDetectionThreshold = leakDetectionThresholdMs;
}
@Deprecated
public void setUseInstrumentation(boolean useInstrumentation)
{
// no longer used as of HikariCP 1.2.5
LOGGER.warn("The useInstrumentation property has been retired, remove it from your pool configuration to avoid this warning.");
}
/** {@inheritDoc} */
@Override
public long getMaxLifetime()
{
return maxLifetime;
}
/** {@inheritDoc} */
@Override
public void setMaxLifetime(long maxLifetimeMs)
{
this.maxLifetime = maxLifetimeMs;
}
/** {@inheritDoc} */
public int getMinimumPoolSize()
{
return minPoolSize;
}
/** {@inheritDoc} */
@Deprecated
public void setMinimumPoolSize(int minPoolSize)
{
if (minPoolSize < 0)
{
throw new IllegalArgumentException("minPoolSize cannot be negative");
}
this.minPoolSize = minPoolSize;
LOGGER.warn("The minimumPoolSize property has been retired, remove it from your pool configuration to avoid this warning.");
}
/** {@inheritDoc} */
@Override
public int getMaximumPoolSize()
{
return maxPoolSize;
}
/** {@inheritDoc} */
@Override
public void setMaximumPoolSize(int maxPoolSize)
{
if (maxPoolSize < 0)
@ -384,6 +515,43 @@ public class HikariConfig implements HikariConfigMBean
}
/** {@inheritDoc} */
@Override
public int getMinimumIdle()
{
return minIdle;
}
/** {@inheritDoc} */
@Override
public void setMinimumIdle(int minIdle)
{
if (minIdle < 0 || minIdle > maxPoolSize)
{
throw new IllegalArgumentException("maxPoolSize cannot be negative or greater than maximumPoolSize");
}
this.minIdle = minIdle;
}
/**
* Get the default password to use for DataSource.getConnection(username, password) calls.
* @return the password
*/
public String getPassword()
{
return password;
}
/**
* Set the default password to use for DataSource.getConnection(username, password) calls.
* @param password the password
*/
public void setPassword(String password)
{
this.password = password;
}
/** {@inheritDoc} */
@Override
public String getPoolName()
{
return poolName;
@ -417,10 +585,32 @@ public class HikariConfig implements HikariConfigMBean
this.transactionIsolationName = isolationLevel;
}
/**
* Get the default username used for DataSource.getConnection(username, password) calls.
*
* @return the username
*/
public String getUsername()
{
return username;
}
/**
* Set the default username used for DataSource.getConnection(username, password) calls.
*
* @param username the username
*/
public void setUsername(String username)
{
this.username = username;
}
public void validate()
{
Logger logger = LoggerFactory.getLogger(getClass());
validateNumerics();
if (connectionCustomizerClassName != null && connectionCustomizer == null)
{
try
@ -435,79 +625,101 @@ public class HikariConfig implements HikariConfigMBean
}
}
if (connectionTimeout == Integer.MAX_VALUE)
if (driverClassName != null && jdbcUrl == null)
{
logger.warn("No connection wait timeout is set, this might cause an infinite wait.");
logger.error("when specifying driverClassName, jdbcUrl must also be specified");
throw new IllegalStateException("when specifying driverClassName, jdbcUrl must also be specified");
}
else if (connectionTimeout < 100)
else if (jdbcUrl != null && driverClassName == null)
{
logger.warn("connectionTimeout is less than 100ms, did you specify the wrong time unit? Using default instead.");
connectionTimeout = CONNECTION_TIMEOUT;
logger.error("when specifying jdbcUrl, driverClassName must also be specified");
throw new IllegalStateException("when specifying jdbcUrl, driverClassName must also be specified");
}
if (dataSource == null && dataSourceClassName == null)
else if (driverClassName != null && jdbcUrl != null)
{
logger.error("one of either dataSource or dataSourceClassName must be specified");
throw new IllegalStateException("one of either dataSource or dataSourceClassName must be specified");
// OK
}
else if (dataSource == null && dataSourceClassName == null)
{
logger.error("one of either dataSource, dataSourceClassName, or jdbcUrl and driverClassName must be specified");
throw new IllegalArgumentException("one of either dataSource or dataSourceClassName must be specified");
}
else if (dataSource != null && dataSourceClassName != null)
{
logger.warn("both dataSource and dataSourceClassName are specified, ignoring dataSourceClassName");
}
if (idleTimeout < 0)
if (connectionTestQuery != null)
{
logger.error("idleTimeout cannot be negative.");
throw new IllegalStateException("idleTimeout cannot be negative.");
isJdbc4connectionTest = false;
}
else if (idleTimeout < 30000 && idleTimeout != 0)
else if (!isJdbc4connectionTest)
{
logger.warn("idleTimeout is less than 30000ms, did you specify the wrong time unit? Using default instead.");
idleTimeout = IDLE_TIMEOUT;
logger.error("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified");
throw new IllegalStateException("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified");
}
if (!isJdbc4connectionTest && connectionTestQuery == null)
if (transactionIsolationName != null)
{
logger.error("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified.");
throw new IllegalStateException("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified.");
try
{
Field field = Connection.class.getField(transactionIsolationName);
int level = field.getInt(null);
this.transactionIsolation = level;
}
catch (Exception e)
{
throw new IllegalArgumentException("Invalid transaction isolation value: " + transactionIsolationName);
}
}
}
if (leakDetectionThreshold != 0 && leakDetectionThreshold < 10000)
private void validateNumerics()
{
Logger logger = LoggerFactory.getLogger(getClass());
if (connectionTimeout == Integer.MAX_VALUE)
{
logger.warn("leakDetectionThreshold is less than 10000ms, did you specify the wrong time unit? Disabling leak detection.");
leakDetectionThreshold = 0;
logger.warn("No connection wait timeout is set, this might cause an infinite wait");
}
else if (connectionTimeout < TimeUnit.MILLISECONDS.toMillis(250))
{
logger.warn("connectionTimeout is less than 250ms, did you specify the wrong time unit? Using default instead");
connectionTimeout = CONNECTION_TIMEOUT;
}
if (maxPoolSize < minPoolSize)
if (minIdle < 0)
{
logger.warn("maxPoolSize is less than minPoolSize, forcing them equal.");
maxPoolSize = minPoolSize;
minIdle = maxPoolSize;
}
if (idleTimeout < 0)
{
logger.error("idleTimeout cannot be negative.");
throw new IllegalArgumentException("idleTimeout cannot be negative");
}
else if (idleTimeout < TimeUnit.SECONDS.toMillis(30) && idleTimeout != 0)
{
logger.warn("idleTimeout is less than 30000ms, did you specify the wrong time unit? Using default instead");
idleTimeout = IDLE_TIMEOUT;
}
if (leakDetectionThreshold != 0 && leakDetectionThreshold < TimeUnit.SECONDS.toMillis(10))
{
logger.warn("leakDetectionThreshold is less than 10000ms, did you specify the wrong time unit? Disabling leak detection");
leakDetectionThreshold = 0;
}
if (maxLifetime < 0)
{
logger.error("maxLifetime cannot be negative.");
throw new IllegalStateException("maxLifetime cannot be negative.");
throw new IllegalArgumentException("maxLifetime cannot be negative.");
}
else if (maxLifetime < 120000 && maxLifetime != 0)
else if (maxLifetime < TimeUnit.SECONDS.toMillis(120) && maxLifetime != 0)
{
logger.warn("maxLifetime is less than 120000ms, did you specify the wrong time unit? Using default instead.");
maxLifetime = MAX_LIFETIME;
}
if (transactionIsolationName != null)
{
try
{
Field field = Connection.class.getField(transactionIsolationName);
int level = field.getInt(null);
this.transactionIsolation = level;
}
catch (Exception e)
{
throw new IllegalArgumentException("Invalid transaction isolation value: " + transactionIsolationName);
}
}
}
IConnectionCustomizer getConnectionCustomizer()

@ -23,32 +23,6 @@ package com.zaxxer.hikari;
*/
public interface HikariConfigMBean
{
/**
* This is a per-connection attempt retry count used during new connection creation (acquisition).
* If a connection creation attempt fails there will be a wait of {@link #getAcquireRetryDelay} milliseconds
* followed by another attempt, up to the number of retries configured by this property.
*
* @return the acquire retry count
*/
int getAcquireRetries();
/**
* This is a per-connection attempt retry count used during new connection creation (acquisition).
* If a connection creation attempt fails there will be a wait of {@link #setAcquireRetryDelay} milliseconds
* followed by another attempt, up to the number of retries configured by this property.
*
* @param acquireRetries the acquire retry count
*/
void setAcquireRetries(int acquireRetries);
/**
* This property controls the number of milliseconds to delay between attempts to acquire a connection
* to the database. If acquireRetries is 0, this property has no effect.
*
* @param acquireRetryDelayMs the acquire retry delay in milliseconds
*/
void setAcquireRetryDelay(long acquireRetryDelayMs);
/**
* This is for "legacy" databases that do not support the JDBC4 {@code Connection.isValid()} API. This is the
* query that will be executed just before a connection is given to you from the pool to validate that
@ -135,7 +109,7 @@ public interface HikariConfigMBean
*
* @return the minimum number of connections in the pool
*/
int getMinimumPoolSize();
int getMinimumIdle();
/**
* The property controls the minimum number of connections that HikariCP tries to maintain in the pool,
@ -144,7 +118,7 @@ public interface HikariConfigMBean
*
* @param minPoolSize the minimum number of connections in the pool
*/
void setMinimumPoolSize(int minPoolSize);
void setMinimumIdle(int minIdle);
/**
* The property controls the minimum number of connections that HikariCP tries to maintain in the pool,

@ -20,11 +20,12 @@ import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.util.DriverDataSource;
/**
* The HikariCP pooled DataSource.
@ -33,12 +34,12 @@ import org.slf4j.LoggerFactory;
*/
public class HikariDataSource extends HikariConfig implements DataSource
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class);
private final HashMap<MultiPoolKey, HikariPool> multiPool;
private volatile boolean isShutdown;
private int loginTimeout;
HikariPool fastPathPool;
/* Package scopped for testing */
final HikariPool fastPathPool;
volatile HikariPool pool;
/**
@ -50,6 +51,8 @@ public class HikariDataSource extends HikariConfig implements DataSource
public HikariDataSource()
{
super();
fastPathPool = null;
multiPool = new HashMap<MultiPoolKey, HikariPool>();
}
/**
@ -59,24 +62,28 @@ public class HikariDataSource extends HikariConfig implements DataSource
*/
public HikariDataSource(HikariConfig configuration)
{
super();
configuration.validate();
configuration.copyState(this);
multiPool = new HashMap<MultiPoolKey, HikariPool>();
pool = fastPathPool = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
}
/** {@inheritDoc} */
@Override
public Connection getConnection() throws SQLException
{
if (isShutdown)
{
throw new IllegalStateException("The datasource has been shutdown.");
throw new SQLException("Pool has been shutdown");
}
if (fastPathPool != null)
{
return fastPathPool.getConnection();
}
// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
HikariPool result = pool;
if (result == null)
{
@ -85,7 +92,9 @@ public class HikariDataSource extends HikariConfig implements DataSource
result = pool;
if (result == null)
{
validate();
pool = result = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
}
}
}
@ -94,20 +103,39 @@ public class HikariDataSource extends HikariConfig implements DataSource
}
/** {@inheritDoc} */
@Override
public Connection getConnection(String username, String password) throws SQLException
{
LOGGER.warn("getConnection() with username and password is not supported, calling getConnection() instead");
if (isShutdown)
{
throw new SQLException("Pool has been shutdown");
}
final MultiPoolKey key = new MultiPoolKey(username, password);
HikariPool hikariPool;
synchronized (multiPool)
{
hikariPool = multiPool.get(key);
if (hikariPool == null)
{
hikariPool = new HikariPool(this, username, password);
multiPool.put(key, hikariPool);
}
}
return getConnection();
return hikariPool.getConnection();
}
/** {@inheritDoc} */
@Override
public PrintWriter getLogWriter() throws SQLException
{
return (pool.dataSource != null ? pool.dataSource.getLogWriter() : null);
}
/** {@inheritDoc} */
@Override
public void setLogWriter(PrintWriter out) throws SQLException
{
if (pool.dataSource != null)
@ -117,12 +145,14 @@ public class HikariDataSource extends HikariConfig implements DataSource
}
/** {@inheritDoc} */
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
this.loginTimeout = seconds;
}
/** {@inheritDoc} */
@Override
public int getLoginTimeout() throws SQLException
{
return loginTimeout;
@ -135,16 +165,38 @@ public class HikariDataSource extends HikariConfig implements DataSource
}
/** {@inheritDoc} */
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException
{
// TODO Auto-generated method stub
return null;
if (pool != null && iface.isInstance(pool.dataSource))
{
return (T) pool.dataSource;
}
throw new SQLException("Wrapped connection is not an instance of " + iface);
}
/** {@inheritDoc} */
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return (this.getClass().isAssignableFrom(iface));
return (pool != null & pool.dataSource.getClass().isAssignableFrom(iface));
}
/**
* Evict a connection from the pool. Use caution using this method, if you
* evict the same connection more than one time, the internal pool accounting
* will become invalid and the pool may stop functioning.
*
* @param connection the connection to evict from the pool
*/
public void evictConnection(Connection connection)
{
if (!isShutdown && pool != null && connection instanceof IHikariConnectionProxy)
{
pool.closeConnection((IHikariConnectionProxy) connection);
}
}
/**
@ -160,12 +212,32 @@ public class HikariDataSource extends HikariConfig implements DataSource
*/
public void shutdown()
{
boolean shutdown = isShutdown;
if (isShutdown)
{
return;
}
isShutdown = true;
if (!shutdown)
if (pool != null)
{
pool.shutdown();
pool = null;
if (pool.dataSource instanceof DriverDataSource)
{
((DriverDataSource) pool.dataSource).shutdown();
}
}
if (!multiPool.isEmpty())
{
for (HikariPool hikariPool : multiPool.values())
{
hikariPool.shutdown();
if (hikariPool.dataSource instanceof DriverDataSource)
{
((DriverDataSource) hikariPool.dataSource).shutdown();
}
}
}
}
@ -175,4 +247,46 @@ public class HikariDataSource extends HikariConfig implements DataSource
{
return String.format("HikariDataSource (%s)", pool);
}
private static class MultiPoolKey
{
private String username;
private String password;
MultiPoolKey(String username, String password)
{
this.username = username;
this.password = password;
}
@Override
public int hashCode()
{
return (password == null ? 0 : password.hashCode());
}
@Override
public boolean equals(Object obj)
{
MultiPoolKey otherKey = ((MultiPoolKey) obj);
if (username != null && !username.equals(otherKey.username))
{
return false;
}
else if (username != otherKey.username)
{
return false;
}
else if (password != null && !password.equals(otherKey.password))
{
return false;
}
else if (password != otherKey.password)
{
return false;
}
return true;
}
}
}

@ -22,6 +22,7 @@ import java.sql.Statement;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -30,10 +31,15 @@ import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.metrics.CodaHaleMetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTracker.Context;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
import com.zaxxer.hikari.util.DriverDataSource;
import com.zaxxer.hikari.util.PoolUtilities;
import com.zaxxer.hikari.util.PropertyBeanSetter;
/**
@ -50,61 +56,60 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private final IConnectionCustomizer connectionCustomizer;
private final HikariConfig configuration;
private final ConcurrentBag<IHikariConnectionProxy> idleConnectionBag;
private final ConcurrentBag<IHikariConnectionProxy> connectionBag;
private final ThreadPoolExecutor addConnectionExecutor;
private final MetricsTracker metricsTracker;
private final Timer houseKeepingTimer;
private final long leakDetectionThreshold;
private final AtomicInteger totalConnections;
private final boolean isAutoCommit;
private final boolean jdbc4ConnectionTest;
private final boolean isIsolateInternalQueries;
private final boolean isReadOnly;
private final boolean isRegisteredMbeans;
private final boolean isJdbc4ConnectionTest;
private final long leakDetectionThreshold;
private final AtomicInteger totalConnections;
private final Timer houseKeepingTimer;
private final String catalog;
private final String username;
private final String password;
private volatile boolean isShutdown;
private volatile long lastConnectionFailureTime;
private int transactionIsolation;
private volatile boolean shutdown;
private boolean debug;
private boolean isDebug;
HikariPool(HikariConfig configuration)
{
this(configuration, configuration.getUsername(), configuration.getPassword());
}
/**
* Construct a HikariPool with the specified configuration.
*
* @param configuration a HikariConfig instance
*/
HikariPool(HikariConfig configuration)
HikariPool(HikariConfig configuration, String username, String password)
{
configuration.validate();
this.configuration = configuration;
this.username = username;
this.password = password;
this.totalConnections = new AtomicInteger();
this.idleConnectionBag = new ConcurrentBag<IHikariConnectionProxy>();
this.idleConnectionBag.addBagStateListener(this);
this.debug = LOGGER.isDebugEnabled();
this.connectionBag = new ConcurrentBag<IHikariConnectionProxy>();
this.connectionBag.addBagStateListener(this);
this.isDebug = LOGGER.isDebugEnabled();
this.catalog = configuration.getCatalog();
this.connectionCustomizer = configuration.getConnectionCustomizer();
this.isAutoCommit = configuration.isAutoCommit();
this.isIsolateInternalQueries = configuration.isIsolateInternalQueries();
this.isReadOnly = configuration.isReadOnly();
this.isRegisteredMbeans = configuration.isRegisterMbeans();
this.jdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.isJdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
this.transactionIsolation = configuration.getTransactionIsolation();
this.metricsTracker = (configuration.isRecordMetrics() ? new CodaHaleMetricsTracker(configuration.getPoolName()) : new MetricsTracker());
if (configuration.getDataSource() == null)
{
String dsClassName = configuration.getDataSourceClassName();
try
{
Class<?> clazz = this.getClass().getClassLoader().loadClass(dsClassName);
this.dataSource = (DataSource) clazz.newInstance();
PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties());
}
catch (Exception e)
{
throw new RuntimeException("Could not create datasource instance: " + dsClassName, e);
}
}
else
{
this.dataSource = configuration.getDataSource();
}
this.dataSource = initializeDataSource();
if (isRegisteredMbeans)
{
@ -113,10 +118,11 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
houseKeepingTimer = new Timer("Hikari Housekeeping Timer", true);
fillPool();
addConnectionExecutor = PoolUtilities.createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler");
long idleTimeout = configuration.getIdleTimeout();
if (idleTimeout > 0 || configuration.getMaxLifetime() > 0)
fillPool();
if (configuration.getIdleTimeout() > 0 || configuration.getMaxLifetime() > 0)
{
long delayPeriod = Long.getLong("com.zaxxer.hikari.housekeeping.period", TimeUnit.SECONDS.toMillis(30));
houseKeepingTimer.scheduleAtFixedRate(new HouseKeeper(), delayPeriod, delayPeriod);
@ -131,21 +137,16 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/
Connection getConnection() throws SQLException
{
if (shutdown)
{
throw new SQLException("Pool has been shutdown");
}
final long start = System.currentTimeMillis();
final Context context = metricsTracker.recordConnectionRequest(start);
long timeout = configuration.getConnectionTimeout();
try
{
long timeout = configuration.getConnectionTimeout();
final long start = System.currentTimeMillis();
do
{
IHikariConnectionProxy connectionProxy = idleConnectionBag.borrow(timeout, TimeUnit.MILLISECONDS);
if (connectionProxy == null)
IHikariConnectionProxy connectionProxy = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);
if (connectionProxy == null) // We timed out... break and throw exception
{
// We timed out... break and throw exception
break;
}
@ -153,34 +154,30 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
if (System.currentTimeMillis() - connectionProxy.getLastAccess() > 1000 && !isConnectionAlive(connectionProxy, timeout))
{
// Throw away the dead connection, try again
closeConnection(connectionProxy);
closeConnection(connectionProxy); // Throw away the dead connection, try again
timeout -= (System.currentTimeMillis() - start);
continue;
}
if (leakDetectionThreshold > 0)
else if (leakDetectionThreshold > 0)
{
connectionProxy.captureStack(leakDetectionThreshold, houseKeepingTimer);
}
return connectionProxy;
}
while (timeout > 0);
logPoolState();
String msg = String.format("Timeout of %dms encountered waiting for connection.", configuration.getConnectionTimeout());
LOGGER.error(msg);
logPoolState("Timeout failure ");
throw new SQLException(msg);
throw new SQLException(String.format("Timeout of %dms encountered waiting for connection.", configuration.getConnectionTimeout()));
}
catch (InterruptedException e)
{
return null;
}
finally
{
context.stop();
}
}
/**
@ -191,9 +188,11 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/
public void releaseConnection(IHikariConnectionProxy connectionProxy)
{
if (!connectionProxy.isBrokenConnection() && !shutdown)
metricsTracker.recordConnectionUsage(System.currentTimeMillis() - connectionProxy.getLastOpenTime());
if (!connectionProxy.isBrokenConnection() && !isShutdown)
{
idleConnectionBag.requite(connectionProxy);
connectionBag.requite(connectionProxy);
}
else
{
@ -210,11 +209,12 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
void shutdown()
{
LOGGER.info("HikariCP pool " + configuration.getPoolName() + " is being shutdown.");
logPoolState("State at shutdown ");
shutdown = true;
isShutdown = true;
houseKeepingTimer.cancel();
addConnectionExecutor.shutdown();
LOGGER.info("HikariCP pool {} is being shutdown.", configuration.getPoolName());
logPoolState("State at shutdown ");
closeIdleConnections();
@ -228,10 +228,37 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
// IBagStateListener methods
// ***********************************************************************
/** {@inheritDoc} */
@Override
public void bagIsEmpty()
public void addBagItem()
{
addConnections(AddConnectionStrategy.ONLY_IF_EMPTY);
class AddConnection implements Runnable {
public void run()
{
int sleepBackoff = 200;
while (totalConnections.get() < configuration.getMaximumPoolSize())
{
final int minIdle = configuration.getMinimumIdle();
if (minIdle != 0 && getIdleConnections() >= minIdle)
{
break;
}
else if (!addConnection())
{
PoolUtilities.quietlySleep(sleepBackoff);
sleepBackoff = (int) Math.min(1000f, ((float) sleepBackoff) * 1.5);
continue;
}
if (minIdle == 0) // This break is here so we only add one connection when demanded
{
break;
}
}
}
}
addConnectionExecutor.submit(new AddConnection());
}
// ***********************************************************************
@ -239,41 +266,44 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
// ***********************************************************************
/** {@inheritDoc} */
@Override
public int getActiveConnections()
{
return Math.min(configuration.getMaximumPoolSize(), totalConnections.get() - getIdleConnections());
}
/** {@inheritDoc} */
@Override
public int getIdleConnections()
{
return idleConnectionBag.values(ConcurrentBag.STATE_NOT_IN_USE).size();
return connectionBag.getCount(ConcurrentBag.STATE_NOT_IN_USE);
}
/** {@inheritDoc} */
@Override
public int getTotalConnections()
{
return totalConnections.get();
}
/** {@inheritDoc} */
@Override
public int getThreadsAwaitingConnection()
{
return idleConnectionBag.getPendingQueue();
return connectionBag.getPendingQueue();
}
/** {@inheritDoc} */
@Override
public void closeIdleConnections()
{
List<IHikariConnectionProxy> list = idleConnectionBag.values(ConcurrentBag.STATE_NOT_IN_USE);
List<IHikariConnectionProxy> list = connectionBag.values(ConcurrentBag.STATE_NOT_IN_USE);
for (IHikariConnectionProxy connectionProxy : list)
{
if (!idleConnectionBag.reserve(connectionProxy))
if (connectionBag.reserve(connectionProxy))
{
continue;
closeConnection(connectionProxy);
}
closeConnection(connectionProxy);
}
}
@ -282,133 +312,53 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
// ***********************************************************************
/**
* Fill the pool up to the minimum size.
* Create and add a single connection to the pool.
*/
private void fillPool()
private boolean addConnection()
{
// maxIters avoids an infinite loop filling the pool if no connections can be acquired
int maxIters = configuration.getMinimumPoolSize() * configuration.getAcquireRetries();
while (maxIters-- > 0 && totalConnections.get() < configuration.getMinimumPoolSize())
Connection connection = null;
try
{
int beforeCount = totalConnections.get();
addConnection();
if (configuration.isInitializationFailFast() && beforeCount == totalConnections.get())
// Speculative increment of totalConnections with expectation of success (first time through)
if (totalConnections.incrementAndGet() > configuration.getMaximumPoolSize())
{
throw new RuntimeException("Fail-fast during pool initialization");
totalConnections.decrementAndGet();
return true;
}
}
logPoolState("Initial fill ");
}
connection = (username == null && password == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);
/**
* Add connections to the pool, not exceeding the maximum allowed.
*/
private void addConnections(AddConnectionStrategy strategy)
{
switch (strategy)
{
case ONLY_IF_EMPTY:
addConnection();
break;
case MAINTAIN_MINIMUM:
final int min = configuration.getMinimumPoolSize();
for (int maxIterations = 0; maxIterations < min && totalConnections.get() < min; maxIterations++)
transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation);
if (connectionCustomizer != null)
{
addConnection();
connectionCustomizer.customize(connection);
}
break;
}
}
/**
* Create and add a single connection to the pool.
*/
private void addConnection()
{
final int acquisitionTimeout = (int) configuration.getConnectionTimeout();
int retries = configuration.getAcquireRetries();
int loginTimeout = 2000;
if (retries == 0)
{
loginTimeout = (acquisitionTimeout == 0 ? Integer.MAX_VALUE : acquisitionTimeout);
PoolUtilities.executeSqlAutoCommit(connection, configuration.getConnectionInitSql());
IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, connection, transactionIsolation, isAutoCommit, isReadOnly, catalog);
proxyConnection.resetConnectionState();
connectionBag.add(proxyConnection);
return true;
}
else if (acquisitionTimeout > 0)
catch (Exception e)
{
loginTimeout = (acquisitionTimeout / (retries + 1));
}
// We failed, so undo speculative increment of totalConnections
totalConnections.decrementAndGet();
while (!shutdown)
{
try
if (connection != null)
{
// Speculative increment of totalConnections with expectation of success
if (totalConnections.incrementAndGet() > configuration.getMaximumPoolSize())
{
totalConnections.decrementAndGet();
break;
}
dataSource.setLoginTimeout(loginTimeout);
Connection connection = dataSource.getConnection();
if (transactionIsolation < 0)
{
transactionIsolation = connection.getTransactionIsolation();
}
if (connectionCustomizer != null)
{
connectionCustomizer.customize(connection);
}
IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, connection, transactionIsolation, isAutoCommit, catalog);
String initSql = configuration.getConnectionInitSql();
if (initSql != null && initSql.length() > 0)
{
connection.setAutoCommit(true);
Statement statement = connection.createStatement();
try
{
statement.execute(initSql);
}
finally
{
statement.close();
}
}
proxyConnection.resetConnectionState();
idleConnectionBag.add(proxyConnection);
break;
PoolUtilities.quietlyCloseConnection(connection);
}
catch (Exception e)
{
if (retries++ > configuration.getAcquireRetries())
{
if (debug)
{
LOGGER.error("Maximum connection creation retries exceeded: {}", e.getMessage(), e);
}
else
{
LOGGER.error("Maximum connection creation retries exceeded: {}", e.getMessage());
}
totalConnections.decrementAndGet();
break;
}
try
{
Thread.sleep(configuration.getAcquireRetryDelay());
}
catch (InterruptedException e1)
{
totalConnections.decrementAndGet();
break;
}
long now = System.currentTimeMillis();
if (now - lastConnectionFailureTime > 1000 || isDebug)
{
LOGGER.warn("Connection attempt to database failed (not every attempt is logged): {}", e.getMessage(), (isDebug ? e : null));
}
lastConnectionFailureTime = now;
return false;
}
}
@ -423,18 +373,14 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
{
try
{
try
{
if (timeoutMs < 1000)
{
timeoutMs = 1000;
}
if (jdbc4ConnectionTest)
{
return connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs));
}
timeoutMs = Math.max(1000, timeoutMs);
if (isJdbc4ConnectionTest)
{
connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs));
}
else
{
Statement statement = connection.createStatement();
try
{
@ -446,12 +392,10 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
statement.close();
}
}
finally
if (isIsolateInternalQueries && !isAutoCommit)
{
if (!isAutoCommit)
{
connection.commit();
}
connection.rollback();
}
return true;
@ -463,12 +407,33 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
}
}
/**
* Fill the pool up to the minimum size.
*/
private void fillPool()
{
if (configuration.isInitializationFailFast())
{
for (int maxIters = configuration.getMinimumIdle(); maxIters > 0; maxIters--)
{
if (!addConnection())
{
throw new RuntimeException("Fail-fast during pool initialization");
}
}
}
else if (configuration.getMinimumIdle() > 0)
{
addBagItem();
}
}
/**
* Permanently close a connection.
*
* @param connectionProxy the connection to actually close
*/
private void closeConnection(IHikariConnectionProxy connectionProxy)
void closeConnection(IHikariConnectionProxy connectionProxy)
{
try
{
@ -481,8 +446,33 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
}
finally
{
idleConnectionBag.remove(connectionProxy);
connectionBag.remove(connectionProxy);
}
}
private DataSource initializeDataSource()
{
String dsClassName = configuration.getDataSourceClassName();
if (configuration.getDataSource() == null && dsClassName != null)
{
try
{
Class<?> clazz = this.getClass().getClassLoader().loadClass(dsClassName);
DataSource dataSource = (DataSource) clazz.newInstance();
PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties());
return dataSource;
}
catch (Exception e)
{
throw new RuntimeException("Could not create datasource instance: " + dsClassName, e);
}
}
else if (configuration.getJdbcUrl() != null)
{
return new DriverDataSource(configuration.getJdbcUrl(), configuration.getDataSourceProperties(), username, password);
}
return configuration.getDataSource();
}
private void logPoolState(String... prefix)
@ -498,9 +488,10 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/
private class HouseKeeper extends TimerTask
{
@Override
public void run()
{
debug = LOGGER.isDebugEnabled();
isDebug = LOGGER.isDebugEnabled();
houseKeepingTimer.purge();
logPoolState("Before pool cleanup ");
@ -509,33 +500,28 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
final long idleTimeout = configuration.getIdleTimeout();
final long maxLifetime = configuration.getMaxLifetime();
for (IHikariConnectionProxy connectionProxy : idleConnectionBag.values(ConcurrentBag.STATE_NOT_IN_USE))
for (IHikariConnectionProxy connectionProxy : connectionBag.values(ConcurrentBag.STATE_NOT_IN_USE))
{
if (!idleConnectionBag.reserve(connectionProxy))
{
continue;
}
if ((idleTimeout > 0 && now > connectionProxy.getLastAccess() + idleTimeout)
|| (maxLifetime > 0 && now > connectionProxy.getCreationTime() + maxLifetime))
if (connectionBag.reserve(connectionProxy))
{
closeConnection(connectionProxy);
}
else
{
idleConnectionBag.unreserve(connectionProxy);
if ((idleTimeout > 0 && now > connectionProxy.getLastAccess() + idleTimeout)
||
(maxLifetime > 0 && now > connectionProxy.getCreationTime() + maxLifetime))
{
closeConnection(connectionProxy);
continue;
}
connectionBag.unreserve(connectionProxy);
}
}
addConnections(AddConnectionStrategy.MAINTAIN_MINIMUM);
logPoolState("After pool cleanup ");
}
}
private static enum AddConnectionStrategy
{
ONLY_IF_EMPTY,
MAINTAIN_MINIMUM
if (getIdleConnections() < configuration.getMinimumIdle() && totalConnections.get() < configuration.getMaximumPoolSize())
{
addBagItem(); // TRY to maintain minimum connections
}
}
}
}

@ -42,20 +42,24 @@ public class HikariConfigurationUtil
@SuppressWarnings("rawtypes")
public static HikariConfig loadConfiguration(Map props)
{
Properties hicaryProps = new Properties();
copyProperty(AvailableSettings.ISOLATION, props, "transactionIsolation", hicaryProps);
copyProperty(AvailableSettings.AUTOCOMMIT, props, "autoCommit", hicaryProps);
Properties hikariProps = new Properties();
copyProperty(AvailableSettings.ISOLATION, props, "transactionIsolation", hikariProps);
copyProperty(AvailableSettings.AUTOCOMMIT, props, "autoCommit", hikariProps);
copyProperty(AvailableSettings.DRIVER, props, "driverClassName", hikariProps);
copyProperty(AvailableSettings.URL, props, "jdbcUrl", hikariProps);
copyProperty(AvailableSettings.USER, props, "username", hikariProps);
copyProperty(AvailableSettings.PASS, props, "password", hikariProps);
for (Object keyo : props.keySet())
{
String key = (String) keyo;
if (key.startsWith(CONFIG_PREFIX))
{
hicaryProps.setProperty(key.substring(CONFIG_PREFIX.length()), (String) props.get(key));
hikariProps.setProperty(key.substring(CONFIG_PREFIX.length()), (String) props.get(key));
}
}
return new HikariConfig(hicaryProps);
return new HikariConfig(hikariProps);
}
@SuppressWarnings("rawtypes")

@ -0,0 +1,66 @@
/*
* Copyright (C) 2013,2014 Brett Wooldridge
*
* 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.zaxxer.hikari.metrics;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.zaxxer.hikari.HikariPool;
public final class CodaHaleMetricsTracker extends MetricsTracker
{
private MetricRegistry registry;
private Timer connectionObtainTimer;
private Histogram connectionUsage;
public CodaHaleMetricsTracker(String poolName)
{
registry = new MetricRegistry();
connectionObtainTimer = registry.timer(MetricRegistry.name(HikariPool.class, "connection", "wait"));
connectionUsage = registry.histogram(MetricRegistry.name(HikariPool.class, "connection", "usage"));
}
@Override
public Context recordConnectionRequest(long requestTime)
{
return new Context(connectionObtainTimer);
}
@Override
public void recordConnectionUsage(long usageMilleseconds)
{
connectionUsage.update(usageMilleseconds);
}
public static final class Context extends MetricsTracker.Context
{
Timer.Context innerContext;
Context(Timer timer)
{
innerContext = timer.time();
}
public void stop()
{
if (innerContext != null)
{
innerContext.stop();
}
}
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) 2013,2014 Brett Wooldridge
*
* 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.zaxxer.hikari.metrics;
/**
* This class does absolutely nothing.
*
* @author Brett Wooldridge
*/
public class MetricsTracker
{
private static final Context NO_CONTEXT = new Context();
public static class Context
{
public void stop()
{
}
}
public Context recordConnectionRequest(long start)
{
return NO_CONTEXT;
}
public void recordConnectionUsage(long usageMilleseconds)
{
}
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013,2014 Brett Wooldridge
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -51,22 +51,24 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
private final HikariPool parentPool;
private final int defaultIsolationLevel;
private final boolean defaultAutoCommit;
private final boolean defaultReadOnly;
private final String defaultCatalog;
private final AtomicInteger state;
private boolean isClosed;
private boolean forceClose;
private boolean isTransactionIsolationDirty;
private boolean isAutoCommitDirty;
private boolean isCatalogDirty;
private boolean isClosed;
private boolean isReadOnlyDirty;
private boolean isTransactionIsolationDirty;
private volatile long lastAccess;
private long uncloseTime;
private StackTraceElement[] leakTrace;
private TimerTask leakTask;
private final int hashCode;
// static initializer
static
{
@ -79,12 +81,13 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
SQL_ERRORS.add("JZ0C1"); // Sybase disconnect error
}
protected ConnectionProxy(HikariPool pool, Connection connection, int defaultIsolationLevel, boolean defaultAutoCommit, String defaultCatalog)
protected ConnectionProxy(HikariPool pool, Connection connection, int defaultIsolationLevel, boolean defaultAutoCommit, boolean defaultReadOnly, String defaultCatalog)
{
this.parentPool = pool;
this.delegate = connection;
this.defaultIsolationLevel = defaultIsolationLevel;
this.defaultAutoCommit = defaultAutoCommit;
this.defaultReadOnly = defaultReadOnly;
this.defaultCatalog = defaultCatalog;
this.state = new AtomicInteger();
@ -93,41 +96,31 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
this.hashCode = System.identityHashCode(this);
isCatalogDirty = true;
isReadOnlyDirty = defaultReadOnly;
isAutoCommitDirty = true;
isTransactionIsolationDirty = true;
}
public final void untrackStatement(Object statement)
{
// If the connection is not closed. If it is closed, it means this is being
// called back as a result of the close() method below in which case we
// will clear the openStatements collection en mass.
if (!isClosed)
{
openStatements.remove(statement);
}
}
public final long getCreationTime()
{
return creationTime;
}
public final long getLastAccess()
/** {@inheritDoc} */
@Override
public final boolean equals(Object other)
{
return lastAccess;
return this == other;
}
public final void unclose()
/** {@inheritDoc} */
@Override
public final int hashCode()
{
isClosed = false;
return hashCode;
}
public final void realClose() throws SQLException
{
delegate.close();
}
// ***********************************************************************
// IHikariConnectionProxy methods
// ***********************************************************************
/** {@inheritDoc} */
@Override
public final void captureStack(long leakDetectionThreshold, Timer scheduler)
{
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
@ -138,11 +131,8 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
scheduler.schedule(leakTask, leakDetectionThreshold);
}
public final boolean isBrokenConnection()
{
return forceClose;
}
/** {@inheritDoc} */
@Override
public final void checkException(SQLException sqle)
{
String sqlState = sqle.getSQLState();
@ -160,33 +150,43 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
}
/** {@inheritDoc} */
@Override
public final boolean equals(Object other)
public final long getCreationTime()
{
return this == other;
return creationTime;
}
/** {@inheritDoc} */
@Override
public final int hashCode()
public final long getLastAccess()
{
return hashCode;
return lastAccess;
}
protected final void checkClosed() throws SQLException
/** {@inheritDoc} */
@Override
public long getLastOpenTime()
{
if (isClosed)
{
throw new SQLException("Connection is closed");
}
return uncloseTime;
}
private <T extends Statement> T trackStatement(T statement)
/** {@inheritDoc} */
@Override
public final boolean isBrokenConnection()
{
openStatements.add(statement);
return forceClose;
}
return statement;
/** {@inheritDoc} */
@Override
public final void realClose() throws SQLException
{
delegate.close();
}
/** {@inheritDoc} */
@Override
public final void resetConnectionState() throws SQLException
{
if (!delegate.getAutoCommit())
@ -194,6 +194,12 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
delegate.rollback();
}
if (isReadOnlyDirty)
{
delegate.setReadOnly(defaultReadOnly);
isReadOnlyDirty = false;
}
if (isAutoCommitDirty)
{
delegate.setAutoCommit(defaultAutoCommit);
@ -215,6 +221,46 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
delegate.clearWarnings();
}
/** {@inheritDoc} */
@Override
public final void unclose()
{
isClosed = false;
uncloseTime = System.currentTimeMillis();
}
/** {@inheritDoc} */
@Override
public final void untrackStatement(Object statement)
{
// If the connection is not closed. If it is closed, it means this is being
// called back as a result of the close() method below in which case we
// will clear the openStatements collection en mass.
if (!isClosed)
{
openStatements.remove(statement);
}
}
// ***********************************************************************
// Internal methods
// ***********************************************************************
protected final void checkClosed() throws SQLException
{
if (isClosed)
{
throw new SQLException("Connection is closed");
}
}
private <T extends Statement> T trackStatement(T statement)
{
openStatements.add(statement);
return statement;
}
// **********************************************************************
// IBagManagable Methods
// **********************************************************************
@ -238,6 +284,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
// **********************************************************************
/** {@inheritDoc} */
@Override
public final void close() throws SQLException
{
if (!isClosed)
@ -253,19 +300,20 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
try
{
final int size = openStatements.size();
for (int i = 0; i < size; i++)
if (size > 0)
{
try
{
openStatements.get(i).close();
}
catch (SQLException e)
for (int i = 0; i < size; i++)
{
checkException(e);
try
{
openStatements.get(i).close();
}
catch (SQLException e)
{
checkException(e);
}
}
}
if (size > 0)
{
openStatements.clear();
}
@ -285,12 +333,14 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final boolean isClosed() throws SQLException
{
return isClosed;
}
/** {@inheritDoc} */
@Override
public final Statement createStatement() throws SQLException
{
checkClosed();
@ -307,6 +357,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
@ -323,6 +374,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
@ -339,6 +391,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final CallableStatement prepareCall(String sql) throws SQLException
{
checkClosed();
@ -355,6 +408,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
@ -371,6 +425,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
@ -387,6 +442,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql) throws SQLException
{
checkClosed();
@ -403,6 +459,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
{
checkClosed();
@ -419,6 +476,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
@ -435,6 +493,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
@ -451,6 +510,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException
{
checkClosed();
@ -467,6 +527,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
{
checkClosed();
@ -483,6 +544,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final boolean isValid(int timeout) throws SQLException
{
if (isClosed)
@ -502,6 +564,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final void setAutoCommit(boolean autoCommit) throws SQLException
{
checkClosed();
@ -518,6 +581,24 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final void setReadOnly(boolean readOnly) throws SQLException
{
checkClosed();
try
{
delegate.setReadOnly(readOnly);
isReadOnlyDirty = (readOnly != defaultReadOnly);
}
catch (SQLException e)
{
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final void setTransactionIsolation(int level) throws SQLException
{
checkClosed();
@ -550,12 +631,14 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
}
/** {@inheritDoc} */
@Override
public final boolean isWrapperFor(Class<?> iface) throws SQLException
{
return iface.isInstance(delegate);
}
/** {@inheritDoc} */
@Override
@SuppressWarnings("unchecked")
public final <T> T unwrap(Class<T> iface) throws SQLException
{

@ -23,26 +23,82 @@ import java.util.Timer;
import com.zaxxer.hikari.util.ConcurrentBag.IBagManagable;
/**
* The interface used by the Connection proxy and through which all interaction
* by other classes flow.
*
* @author Brett Wooldridge
*/
public interface IHikariConnectionProxy extends Connection, IBagManagable
{
void unclose();
void realClose() throws SQLException;
void untrackStatement(Object statement);
/**
* Catpure the stack and start leak detection.
*
* @param leakThreshold the number of milliseconds before a leak is reported
* @param houseKeepingTimer the timer to run the leak detection task with
*/
void captureStack(long leakThreshold, Timer houseKeepingTimer);
/**
* Check if the provided SQLException contains a SQLSTATE that indicates
* a disconnection from the server.
*
* @param sqle the SQLException to check
*/
void checkException(SQLException sqle);
boolean isBrokenConnection();
/**
* Get the creation timestamp of the connection.
*
* @return the creation timestamp
*/
long getCreationTime();
/**
* Get the last access timestamp of the connection.
*
* @return the last access timestamp
*/
long getLastAccess();
/**
* Get the timestamp of when the connection was removed from the pool for use.
*
* @return the timestamp the connection started to be used in the most recent request
*/
long getLastOpenTime();
/**
* Return the broken state of the connection. If checkException() detected
* a broken connection, this method will return true, otherwise false.
*
* @return the broken state of the connection
*/
boolean isBrokenConnection();
/**
* Actually close the underlying delegate Connection.
*
* @throws SQLException rethrown from the underlying delegate Connection
*/
void realClose() throws SQLException;
/**
* Reset the delegate Connection back to pristine state.
*
* @throws SQLException thrown if there is an error resetting any of the state
*/
void resetConnectionState() throws SQLException;
/* Leak Detection API */
void captureStack(long leakThreshold, Timer houseKeepingTimer);
/**
* Make the Connection available for use again by marking it as not closed.
*/
void unclose();
/**
* Called by Statement and its subclasses when they are closed to remove them
* from the tracking list.
*
* @param statement the Statement to remove from tracking
*/
void untrackStatement(Object statement);
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Brett Wooldridge
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,6 +36,10 @@ import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.util.ClassLoaderUtils;
/**
* This class generates the proxy objects for {@link Connection}, {@link Statement},
* {@link PreparedStatement}, and {@link CallableStatement}. Additionally it injects
* method bodies into the {@link ProxyFactory} class methods that can instantiate
* instances of the generated proxies.
*
* @author Brett Wooldridge
*/
@ -45,27 +49,25 @@ public final class JavassistProxyFactory
static
{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(JavassistProxyFactory.class.getClassLoader());
JavassistProxyFactory proxyFactoryFactory = new JavassistProxyFactory();
proxyFactoryFactory.modifyProxyFactory();
}
catch (Exception e)
{
LoggerFactory.getLogger(JavassistProxyFactory.class).error("Fatal exception during proxy generation", e);
throw new RuntimeException(e);
}
finally
{
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
/**
* Simply invoking this method causes the initialization of this class. All work
* by this class is performed in static initialization.
*/
public static void initialize()
{
// simply invoking this method causes the initialization of this class.
// no-op
}
private JavassistProxyFactory()
@ -102,30 +104,22 @@ public final class JavassistProxyFactory
for (CtMethod method : proxyCt.getMethods())
{
String methodName = method.getName();
StringBuilder call = new StringBuilder("{");
if ("getProxyConnection".equals(methodName))
{
call.append("return new ").append(packageName).append(".ConnectionJavassistProxy($$);");
method.setBody("{return new " + packageName + ".ConnectionJavassistProxy($$);}");
}
else if ("getProxyStatement".equals(methodName))
{
call.append("return new ").append(packageName).append(".StatementJavassistProxy($$);");
method.setBody("{return new " + packageName + ".StatementJavassistProxy($$);}");
}
else if ("getProxyPreparedStatement".equals(methodName))
{
call.append("return new ").append(packageName).append(".PreparedStatementJavassistProxy($$);");
method.setBody("{return new " + packageName + ".PreparedStatementJavassistProxy($$);}");
}
else if ("getProxyCallableStatement".equals(methodName))
{
call.append("return new ").append(packageName).append(".CallableStatementJavassistProxy($$);");
method.setBody("{return new " + packageName + ".CallableStatementJavassistProxy($$);}");
}
else
{
continue;
}
call.append('}');
method.setBody(call.toString());
}
proxyCt.toClass(classPool.getClassLoader(), null);

@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory;
/**
* @author Brett Wooldridge
*/
public class LeakTask extends TimerTask
class LeakTask extends TimerTask
{
private final long leakTime;
private StackTraceElement[] stackTrace;

@ -37,6 +37,7 @@ public abstract class PreparedStatementProxy extends StatementProxy implements P
// **********************************************************************
/** {@inheritDoc} */
@Override
public final ResultSet executeQuery() throws SQLException
{
try

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Brett Wooldridge
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,13 +24,30 @@ import java.sql.Statement;
import com.zaxxer.hikari.HikariPool;
/**
* Injected proxy factory class.
* A factory class that produces proxies around instances of the standard
* JDBC interfaces.
*
* @author Brett Wooldridge
*/
public final class ProxyFactory
{
public static IHikariConnectionProxy getProxyConnection(HikariPool pool, Connection connection, int defaultIsolationLevel, boolean defaultAutoCommit, String defaultCatalog)
private ProxyFactory()
{
// unconstructable
}
/**
* Create a proxy for the specified {@link Connection} instance.
*
* @param pool the {@link HikariPool} that will own this proxy
* @param connection the {@link Connection} that will be wrapped by this proxy
* @param defaultIsolationLevel the default transaction isolation level of the underlying {@link Connection}
* @param defaultAutoCommit the default auto-commit state of the underlying {@link Connection}
* @param defaultIReadOnly the default readOnly state of the underlying {@link Connection}
* @param defaultCatalog the default catalog of the underlying {@link Connection}
* @return a proxy that wraps the specified {@link Connection}
*/
public static IHikariConnectionProxy getProxyConnection(HikariPool pool, Connection connection, int defaultIsolationLevel, boolean defaultAutoCommit, boolean defaultIReadOnly, String defaultCatalog)
{
// Body is injected by JavassistProxyFactory
return null;

@ -50,6 +50,7 @@ public abstract class StatementProxy implements Statement
// **********************************************************************
/** {@inheritDoc} */
@Override
public final void close() throws SQLException
{
if (isClosed)
@ -72,6 +73,7 @@ public abstract class StatementProxy implements Statement
}
/** {@inheritDoc} */
@Override
public final ResultSet executeQuery(String sql) throws SQLException
{
try
@ -86,6 +88,7 @@ public abstract class StatementProxy implements Statement
}
/** {@inheritDoc} */
@Override
public final ResultSet getResultSet() throws SQLException
{
try
@ -100,6 +103,7 @@ public abstract class StatementProxy implements Statement
}
/** {@inheritDoc} */
@Override
public final ResultSet getGeneratedKeys() throws SQLException
{
try
@ -114,6 +118,7 @@ public abstract class StatementProxy implements Statement
}
/** {@inheritDoc} */
@Override
public final Connection getConnection() throws SQLException
{
return connection;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Brett Wooldridge
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,12 +28,12 @@ import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
* to LinkedBlockingQueue and LinkedTransferQueue for the purposes of a
* connection pool. It uses ThreadLocal storage when possible to avoid
* locks, but resorts to scanning a common collection if there are no
* available connections in the ThreadLocal list. Idle connections in
* ThreadLocal lists can be "stolen" when the poll()ing thread has none
* available items in the ThreadLocal list. Not-in-use items in the
* ThreadLocal lists can be "stolen" when the borrowing thread has none
* of its own. It is a "lock-less" implementation using a specialized
* AbstractQueuedLongSynchronizer to manage cross-thread signaling.
*
* Note that objects that are "borrowed" from the bag are not actually
* Note that items that are "borrowed" from the bag are not actually
* removed from any collection, so garbage collection will not occur
* even if the reference is abandoned. Thus care must be taken to
* "requite" borrowed objects otherwise a memory leak will result. Only
@ -64,9 +64,18 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
boolean compareAndSetState(int expectedState, int newState);
}
/**
* This interface is implemented by a listener to the bag. The listener
* will be informed of when the bag has become empty. The usual course
* of action by the listener in this case is to attempt to add an item
* to the bag.
*/
public interface IBagStateListener
{
void bagIsEmpty();
/**
* @param timeout timeout to add item to bag in milliseconds
*/
void addBagItem();
}
private ThreadLocal<LinkedList<WeakReference<T>>> threadList;
@ -127,7 +136,7 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
if (listener != null)
{
listener.bagIsEmpty();
listener.addBagItem();
}
synchronizer.tryAcquireSharedNanos(startScan, timeout);
@ -157,7 +166,14 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
if (value.compareAndSetState(STATE_IN_USE, STATE_NOT_IN_USE))
{
final long returnTime = System.nanoTime();
threadList.get().addLast(new WeakReference<T>(value));
LinkedList<WeakReference<T>> list = threadList.get();
if (list == null)
{
list = new LinkedList<WeakReference<T>>();
threadList.set(list);
}
list.addLast(new WeakReference<T>(value));
synchronizer.releaseShared(returnTime);
}
else
@ -180,7 +196,7 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
/**
* Remove a value from the bag. This method should only be called
* with objects obtained by borrow() or reserve().
* with objects obtained by {@link #borrow(long, TimeUnit)} or {@link #reserve(IBagManagable)}.
* @param value the value to remove
* @throws IllegalStateException if an attempt is made to remove an object
* from the bag that was not borrowed or reserved first
@ -200,7 +216,8 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
/**
* This method provides a "snaphot" in time of the IBagManagable
* items in the bag in the specified state. It does not "lock"
* or reserve items in any way.
* or reserve items in any way. Call {@link #reserve(IBagManagable)}
* on items in list before performing any action on them.
*
* @param state one of STATE_NOT_IN_USE or STATE_IN_USE
* @return a possibly empty list of objects having the state specified
@ -221,11 +238,29 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
return list;
}
/**
* The method is used to make an item in the bag "unavailable" for
* borrowing. It is primarily used when wanting to operate on items
* returned by the {@link #values(int)} method. Items that are
* reserved can be removed from the bag via {@link #remove(IBagManagable)}
* without the need to unreserve them. Items that are not removed
* from the bag can be make available for borrowing again by calling
* the {@link #unreserve(IBagManagable) method.
*
* @param value the item to reserve
* @return true if the item was able to be reserved, false otherwise
*/
public boolean reserve(T value)
{
return value.compareAndSetState(STATE_NOT_IN_USE, STATE_RESERVED);
}
/**
* This method is used to make an item reserved via {@link #reserve(IBagManagable)}
* available again for borrowing.
*
* @param value the item to unreserve
*/
public void unreserve(T value)
{
final long checkInTime = System.nanoTime();
@ -237,27 +272,75 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
synchronizer.releaseShared(checkInTime);
}
/**
* Add a listener to the bag. There can only be one. If this method is
* called a second time, the original listener will be evicted.
*
* @param listener a listener to the bag
*/
public void addBagStateListener(IBagStateListener listener)
{
this.listener = listener;
}
/**
* Get the number of threads pending (waiting) for an item from the
* bag to become available.
*
* @return the number of threads waiting for items from the bag
*/
public int getPendingQueue()
{
return synchronizer.getQueueLength();
}
public int getCount(int state)
{
int count = 0;
for (T reference : sharedList)
{
if (reference.getState() == state)
{
count++;
}
}
return count;
}
/**
* Get the total number of items in the bag.
*
* @return the number of items in the bag
*/
public int size()
{
return sharedList.size();
}
/**
* Our private synchronizer that handles notify/wait type semantics.
*/
private static class Synchronizer extends AbstractQueuedLongSynchronizer
{
private static final long serialVersionUID = 104753538004341218L;
private static boolean JAVA7;
static
{
try
{
JAVA7 = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class<?>[0]) != null;
}
catch (Exception e)
{
// nothing
}
}
@Override
protected long tryAcquireShared(long startScanTime)
{
return getState() > startScanTime ? 1 : -1;
return getState() >= startScanTime && !java67hasQueuedPredecessors() ? 1 : -1;
}
/** {@inheritDoc} */
@ -268,5 +351,15 @@ public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagMa
return true;
}
private boolean java67hasQueuedPredecessors()
{
if (JAVA7)
{
return hasQueuedPredecessors();
}
return false;
}
}
}

@ -0,0 +1,116 @@
/*
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* 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.zaxxer.hikari.util;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import javax.sql.DataSource;
public final class DriverDataSource implements DataSource
{
private final String jdbcUrl;
private final Properties driverProperties;
private PrintWriter logWriter;
public DriverDataSource(String jdbcUrl, Properties properties, String username, String password)
{
try
{
this.jdbcUrl = jdbcUrl;
this.driverProperties = new Properties(properties);
if (username != null)
{
driverProperties.put("user", driverProperties.getProperty("user", username));
}
if (password != null)
{
driverProperties.put("password", driverProperties.getProperty("password", password));
}
if (DriverManager.getDriver(jdbcUrl) == null)
{
throw new IllegalArgumentException("DriverManager was unable to load driver for URL " + jdbcUrl);
}
}
catch (SQLException e)
{
throw new RuntimeException("Unable to get driver for JDBC URL "+ jdbcUrl);
}
}
@Override
public Connection getConnection() throws SQLException
{
return DriverManager.getConnection(jdbcUrl, driverProperties);
}
@Override
public Connection getConnection(String username, String password) throws SQLException
{
return getConnection();
}
@Override
public PrintWriter getLogWriter() throws SQLException
{
return logWriter;
}
@Override
public void setLogWriter(PrintWriter logWriter) throws SQLException
{
this.logWriter = logWriter;
}
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
DriverManager.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException
{
return DriverManager.getLoginTimeout();
}
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
{
throw new SQLFeatureNotSupportedException();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
throw new SQLFeatureNotSupportedException();
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
}
public void shutdown()
{
}
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Brett Wooldridge
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,7 +31,7 @@ public final class FastStatementList
private int size;
/**
* Construct a FastList with a default size of 16.
* Construct a FastList with a default size of 32.
*/
public FastStatementList()
{

@ -0,0 +1,77 @@
package com.zaxxer.hikari.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public final class PoolUtilities
{
public static void quietlyCloseConnection(Connection connection)
{
try
{
connection.close();
}
catch (SQLException e)
{
return;
}
}
/**
* Execute the user-specified init SQL.
*
* @param connection the connection to initialize
* @throws SQLException throws if the init SQL execution fails
*/
public static void executeSqlAutoCommit(Connection connection, String sql) throws SQLException
{
if (sql != null)
{
connection.setAutoCommit(true);
Statement statement = connection.createStatement();
try
{
statement.execute(sql);
}
finally
{
statement.close();
}
}
}
public static void quietlySleep(int millis)
{
try
{
Thread.sleep(millis);
}
catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
public static ThreadPoolExecutor createThreadPoolExecutor(final int queueSize, final String threadName)
{
ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable r)
{
Thread t = new Thread(r, threadName);
t.setDaemon(true);
return t;
}
};
int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 10, TimeUnit.SECONDS, queue, threadFactory, new ThreadPoolExecutor.DiscardPolicy());
executor.allowCoreThreadTimeOut(true);
return executor;
}
}

@ -61,9 +61,27 @@ public final class PropertyBeanSetter
private static void setProperty(Object target, String propName, Object propValue)
{
String capitalized = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
PropertyDescriptor propertyDescriptor;
try
{
propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
}
catch (IntrospectionException e)
{
capitalized = "set" + propName.toUpperCase();
try
{
propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
}
catch (IntrospectionException e1)
{
LOGGER.error("Property {} is does not exist on target class {}", propName, target.getClass());
throw new RuntimeException(e);
}
}
try
{
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
Method writeMethod = propertyDescriptor.getWriteMethod();
Class<?> paramClass = writeMethod.getParameterTypes()[0];
if (paramClass == int.class)
@ -87,11 +105,6 @@ public final class PropertyBeanSetter
writeMethod.invoke(target, propValue);
}
}
catch (IntrospectionException e)
{
LOGGER.error("Property {} is does not exist on target class {}", propName, target.getClass());
throw new RuntimeException(e);
}
catch (Exception e)
{
LOGGER.error("Exception setting property {} on target class {}", propName, target.getClass(), e);

@ -13,9 +13,8 @@ public class ConnectionStateTest
{
HikariDataSource ds = new HikariDataSource();
ds.setAutoCommit(true);
ds.setMinimumPoolSize(1);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setAcquireIncrement(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -41,9 +40,8 @@ public class ConnectionStateTest
{
HikariDataSource ds = new HikariDataSource();
ds.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
ds.setMinimumPoolSize(1);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setAcquireIncrement(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -64,14 +62,25 @@ public class ConnectionStateTest
}
}
@Test
public void testIsolation() throws Exception
{
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setTransactionIsolation("TRANSACTION_REPEATABLE_READ");
config.validate();
int transactionIsolation = config.getTransactionIsolation();
Assert.assertSame(Connection.TRANSACTION_REPEATABLE_READ, transactionIsolation);
}
@Test
public void testCatalog() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setCatalog("test");
ds.setMinimumPoolSize(1);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setAcquireIncrement(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");

@ -18,9 +18,9 @@ public class ExceptionTest
public void setup()
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -91,9 +91,9 @@ public class ExceptionTest
public void testUseAfterClose() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");

@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 Brett Wooldridge
*
* 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.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.util.DriverDataSource;
public class JdbcDriverTest
{
@Test
public void driverTest1() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setConnectionTestQuery("VALUES 1");
config.setDriverClassName("com.zaxxer.hikari.mocks.StubDriver");
config.setJdbcUrl("jdbc:stub");
config.addDataSourceProperty("user", "bart");
config.addDataSourceProperty("password", "simpson");
HikariDataSource ds = new HikariDataSource(config);
Assert.assertTrue(ds.isWrapperFor(DriverDataSource.class));
DriverDataSource unwrap = ds.unwrap(DriverDataSource.class);
Assert.assertNotNull(unwrap);
Connection connection = ds.getConnection();
connection.close();
ds.shutdown();
}
}

@ -12,9 +12,9 @@ public class RampUpDown
public void rampUpDownTest() throws SQLException, InterruptedException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(5);
config.setMinimumIdle(5);
config.setMaximumPoolSize(60);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");

@ -13,9 +13,9 @@ public class StatementTest
public void testStatementClose() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -42,9 +42,9 @@ public class StatementTest
public void testAutoStatementClose() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -67,9 +67,9 @@ public class StatementTest
public void testDoubleStatementClose() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -88,9 +88,9 @@ public class StatementTest
public void testOutOfOrderStatementClose() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");

@ -0,0 +1,270 @@
package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.mocks.StubDataSource;
public class TestConnectionTimeoutRetry
{
@Test
public void testConnectionRetries() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(2800);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
stubDataSource.setThrowException(new SQLException("Connection refused"));
long start = System.currentTimeMillis();
try
{
Connection connection = ds.getConnection();
connection.close();
Assert.fail("Should not have been able to get a connection.");
}
catch (SQLException e)
{
long elapsed = System.currentTimeMillis() - start;
long timeout = config.getConnectionTimeout();
Assert.assertTrue("Didn't wait long enough for timeout", (elapsed >= timeout));
}
finally
{
ds.shutdown();
}
}
@Test
public void testConnectionRetries2() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(2800);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
final StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
stubDataSource.setThrowException(new SQLException("Connection refused"));
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(new Runnable() {
public void run()
{
stubDataSource.setThrowException(null);
}
}, 300, TimeUnit.MILLISECONDS);
long start = System.currentTimeMillis();
try
{
Connection connection = ds.getConnection();
connection.close();
long elapsed = System.currentTimeMillis() - start;
Assert.assertTrue("Connection returned too quickly, something is wrong.", elapsed > 250);
Assert.assertTrue("Waited too long to get a connection.", elapsed < config.getConnectionTimeout());
}
catch (SQLException e)
{
Assert.fail("Should not have timed out.");
}
finally
{
scheduler.shutdownNow();
ds.shutdown();
}
}
@Test
public void testConnectionRetries3() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(2);
config.setConnectionTimeout(2800);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
final Connection connection1 = ds.getConnection();
final Connection connection2 = ds.getConnection();
Assert.assertNotNull(connection1);
Assert.assertNotNull(connection2);
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.schedule(new Runnable() {
public void run()
{
try
{
connection1.close();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
}, 800, TimeUnit.MILLISECONDS);
long start = System.currentTimeMillis();
try
{
Connection connection3 = ds.getConnection();
connection3.close();
long elapsed = System.currentTimeMillis() - start;
Assert.assertTrue("Waited too long to get a connection.", (elapsed >= 700) && (elapsed < 950));
}
catch (SQLException e)
{
Assert.fail("Should not have timed out.");
}
finally
{
scheduler.shutdownNow();
ds.shutdown();
}
}
@Test
public void testConnectionRetries4() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(1000);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
stubDataSource.setThrowException(new SQLException("Connection refused"));
long start = System.currentTimeMillis();
try
{
Connection connection = ds.getConnection();
connection.close();
Assert.fail("Should not have been able to get a connection.");
}
catch (SQLException e)
{
long elapsed = System.currentTimeMillis() - start;
Assert.assertTrue("Didn't wait long enough for timeout", (elapsed >= config.getConnectionTimeout()));
}
finally
{
ds.shutdown();
}
}
@Test
public void testConnectionRetries5() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(2);
config.setConnectionTimeout(1000);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
final Connection connection1 = ds.getConnection();
long start = System.currentTimeMillis();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.schedule(new Runnable() {
public void run()
{
try
{
connection1.close();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
}, 250, TimeUnit.MILLISECONDS);
StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
stubDataSource.setThrowException(new SQLException("Connection refused"));
try
{
Connection connection2 = ds.getConnection();
connection2.close();
long elapsed = System.currentTimeMillis() - start;
Assert.assertTrue("Waited too long to get a connection.", (elapsed >= 250) && (elapsed < config.getConnectionTimeout()));
}
catch (SQLException e)
{
Assert.fail("Should not have timed out.");
}
finally
{
scheduler.shutdownNow();
ds.shutdown();
}
}
@Test
public void testConnectionIdleFill() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(10);
config.setConnectionTimeout(1000);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
Connection connection1 = ds.getConnection();
Connection connection2 = ds.getConnection();
Connection connection3 = ds.getConnection();
Connection connection4 = ds.getConnection();
Connection connection5 = ds.getConnection();
Connection connection6 = ds.getConnection();
Connection connection7 = ds.getConnection();
Thread.sleep(1000);
Assert.assertSame("Totals connections not as expected", 10, ds.pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 3, ds.pool.getIdleConnections());
connection1.close();
connection2.close();
connection3.close();
connection4.close();
connection5.close();
connection6.close();
connection7.close();
Assert.assertSame("Totals connections not as expected", 10, ds.pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 10, ds.pool.getIdleConnections());
}
}

@ -24,6 +24,8 @@ import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.mocks.StubConnection;
/**
* System property testProxy can be one of:
* "com.zaxxer.hikari.JavaProxyFactory"
@ -32,15 +34,15 @@ import org.junit.Test;
*
* @author Brett Wooldridge
*/
public class CreationTest
public class TestConnections
{
@Test
public void testCreate() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -83,9 +85,9 @@ public class CreationTest
public void testMaxLifetime() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -113,10 +115,12 @@ public class CreationTest
Connection connection2 = ds.getConnection();
Assert.assertSame("Expected the same connection", connection, connection2);
Assert.assertSame("Second total connections not as expected", 1, ds.pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, ds.pool.getIdleConnections());
connection2.close();
Thread.sleep(2000);
connection2 = ds.getConnection();
Assert.assertNotSame("Expected a different connection", connection, connection2);
@ -135,9 +139,9 @@ public class CreationTest
public void testDoubleClose() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -158,9 +162,10 @@ public class CreationTest
public void testBackfill() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setConnectionTimeout(500);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -199,12 +204,8 @@ public class CreationTest
Assert.assertSame("Totals connections not as expected", 0, ds.pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, ds.pool.getIdleConnections());
// This will create a new connection, and cause a backfill
// This will cause a backfill
connection = ds.getConnection();
// Wait for scheduled backfill to execute
Thread.sleep(600);
connection.close();
Assert.assertSame("Totals connections not as expected", 1, ds.pool.getTotalConnections());
@ -217,14 +218,50 @@ public class CreationTest
}
@Test
public void testIsolation() throws Exception
public void testMaximumPoolLimit() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setTransactionIsolation("TRANSACTION_REPEATABLE_READ");
config.validate();
int transactionIsolation = config.getTransactionIsolation();
Assert.assertSame(Connection.TRANSACTION_REPEATABLE_READ, transactionIsolation);
StubConnection.count.set(0);
final HikariDataSource ds = new HikariDataSource(config);
Thread[] threads = new Thread[20];
for (int i = 0; i < threads.length; i++)
{
threads[i] = new Thread(new Runnable() {
public void run()
{
try
{
Connection connection = ds.getConnection();
Thread.sleep(1000);
connection.close();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
for (int i = 0; i < threads.length; i++)
{
threads[i].start();
}
for (int i = 0; i < threads.length; i++)
{
threads[i].join();
}
Assert.assertEquals(4, StubConnection.count.get());
}
}

@ -20,7 +20,7 @@ public class TestPropertySetter
HikariConfig config = new HikariConfig(file.getPath());
config.validate();
Assert.assertEquals(5, config.getAcquireRetries());
Assert.assertEquals(5, config.getMinimumIdle());
Assert.assertEquals("SELECT 1", config.getConnectionTestQuery());
}
@ -50,4 +50,17 @@ public class TestPropertySetter
Assert.assertSame(PrintWriter.class, dataSource.getLogWriter().getClass());
}
@Test
public void testPropertyUpperCase() throws Exception
{
File file = new File("src/test/resources/propfile3.properties");
HikariConfig config = new HikariConfig(file.getPath());
config.validate();
Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
DataSource dataSource = (DataSource) clazz.newInstance();
PropertyBeanSetter.setTargetFromProperties(dataSource, config.getDataSourceProperties());
}
}

@ -34,9 +34,9 @@ public class UnwrapTest
public void testUnwrapConnection() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");

@ -44,29 +44,35 @@ import org.mockito.stubbing.Answer;
*/
public class MockDataSource implements DataSource
{
@Override
public Connection getConnection() throws SQLException
{
return createMockConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException
{
return getConnection();
}
@Override
public PrintWriter getLogWriter() throws SQLException
{
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException
{
}
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
}
@Override
public int getLoginTimeout() throws SQLException
{
return 0;
@ -77,11 +83,13 @@ public class MockDataSource implements DataSource
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
@ -110,6 +118,7 @@ public class MockDataSource implements DataSource
when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt())).thenReturn(mockPreparedStatement);
when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(mockPreparedStatement);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable
{
return null;

@ -8,12 +8,14 @@ import java.sql.Statement;
public abstract class StubBaseConnection implements Connection
{
/** {@inheritDoc} */
@Override
public Statement createStatement() throws SQLException
{
return new StubStatement(this);
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException
{
return new StubPreparedStatement(this);

@ -34,6 +34,7 @@ import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
@ -41,6 +42,8 @@ import java.util.concurrent.Executor;
*/
public class StubConnection extends StubBaseConnection implements Connection
{
public static AtomicInteger count = new AtomicInteger();
private static long foo;
private boolean autoCommit;
private int isolation;
@ -51,275 +54,327 @@ public class StubConnection extends StubBaseConnection implements Connection
foo = System.currentTimeMillis();
}
public StubConnection()
{
count.incrementAndGet();
}
/** {@inheritDoc} */
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public CallableStatement prepareCall(String sql) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public String nativeSQL(String sql) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException
{
this.autoCommit = autoCommit;
}
/** {@inheritDoc} */
@Override
public boolean getAutoCommit() throws SQLException
{
return autoCommit;
}
/** {@inheritDoc} */
@Override
public void commit() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void rollback() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void close() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public boolean isClosed() throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public DatabaseMetaData getMetaData() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void setReadOnly(boolean readOnly) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public boolean isReadOnly() throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public void setCatalog(String catalog) throws SQLException
{
this.catalog = catalog;
}
/** {@inheritDoc} */
@Override
public String getCatalog() throws SQLException
{
return catalog;
}
/** {@inheritDoc} */
@Override
public void setTransactionIsolation(int level) throws SQLException
{
this.isolation = level;
}
/** {@inheritDoc} */
@Override
public int getTransactionIsolation() throws SQLException
{
return isolation;
}
/** {@inheritDoc} */
@Override
public SQLWarning getWarnings() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void clearWarnings() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
return new StubPreparedStatement(this);
}
/** {@inheritDoc} */
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setHoldability(int holdability) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int getHoldability() throws SQLException
{
return (int) foo;
}
/** {@inheritDoc} */
@Override
public Savepoint setSavepoint() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public Savepoint setSavepoint(String name) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void rollback(Savepoint savepoint) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
return new StubPreparedStatement(this);
}
/** {@inheritDoc} */
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
{
return new StubPreparedStatement(this);
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException
{
return new StubPreparedStatement(this);
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
{
return new StubPreparedStatement(this);
}
/** {@inheritDoc} */
@Override
public Clob createClob() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public Blob createBlob() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public NClob createNClob() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public SQLXML createSQLXML() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public boolean isValid(int timeout) throws SQLException
{
return true;
}
/** {@inheritDoc} */
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException
{
}
/** {@inheritDoc} */
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException
{
}
/** {@inheritDoc} */
@Override
public String getClientInfo(String name) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public Properties getClientInfo() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException
{
return null;

@ -33,6 +33,7 @@ public class StubDataSource implements DataSource
private String user;
private String password;
private PrintWriter logWriter;
private SQLException throwException;
public String getUser()
{
@ -54,24 +55,33 @@ public class StubDataSource implements DataSource
this.password = password;
}
public void setURL(String url)
{
// we don't care
}
/** {@inheritDoc} */
@Override
public PrintWriter getLogWriter() throws SQLException
{
return logWriter;
}
/** {@inheritDoc} */
@Override
public void setLogWriter(PrintWriter out) throws SQLException
{
this.logWriter = out;
}
/** {@inheritDoc} */
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int getLoginTimeout() throws SQLException
{
return 0;
@ -84,26 +94,40 @@ public class StubDataSource implements DataSource
}
/** {@inheritDoc} */
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public Connection getConnection() throws SQLException
{
if (throwException != null)
{
throw throwException;
}
return new StubConnection();
}
/** {@inheritDoc} */
@Override
public Connection getConnection(String username, String password) throws SQLException
{
return new StubConnection();
}
public void setThrowException(SQLException e)
{
this.throwException = e;
}
}

@ -47,36 +47,42 @@ public class StubDriver implements Driver
}
/** {@inheritDoc} */
@Override
public Connection connect(String url, Properties info) throws SQLException
{
return new StubConnection();
}
/** {@inheritDoc} */
@Override
public boolean acceptsURL(String url) throws SQLException
{
return true;
}
/** {@inheritDoc} */
@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public int getMajorVersion()
{
return 0;
}
/** {@inheritDoc} */
@Override
public int getMinorVersion()
{
return 0;
}
/** {@inheritDoc} */
@Override
public boolean jdbcCompliant()
{
return true;

@ -51,95 +51,112 @@ public class StubPreparedStatement extends StubStatement implements PreparedStat
}
/** {@inheritDoc} */
@Override
public ResultSet executeQuery(String sql) throws SQLException
{
return new StubResultSet();
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql) throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public int getMaxFieldSize() throws SQLException
{
throw new SQLException("No reason", "08999");
}
/** {@inheritDoc} */
@Override
public void setMaxFieldSize(int max) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int getMaxRows() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public void setMaxRows(int max) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setEscapeProcessing(boolean enable) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int getQueryTimeout() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public void setQueryTimeout(int seconds) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void cancel() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public SQLWarning getWarnings() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void clearWarnings() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setCursorName(String name) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public ResultSet getResultSet() throws SQLException
{
return new StubResultSet();
}
/** {@inheritDoc} */
@Override
public int getUpdateCount() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public boolean getMoreResults() throws SQLException
{
if (isClosed())
@ -150,419 +167,498 @@ public class StubPreparedStatement extends StubStatement implements PreparedStat
}
/** {@inheritDoc} */
@Override
public void setFetchDirection(int direction) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int getFetchDirection() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public void setFetchSize(int rows) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int getFetchSize() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public int getResultSetConcurrency() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public int getResultSetType() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public void addBatch(String sql) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void clearBatch() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public int[] executeBatch() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public boolean getMoreResults(int current) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public ResultSet getGeneratedKeys() throws SQLException
{
return new StubResultSet();
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql, String[] columnNames) throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql, String[] columnNames) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public int getResultSetHoldability() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public void setPoolable(boolean poolable) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public boolean isPoolable() throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public void closeOnCompletion() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public boolean isCloseOnCompletion() throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public ResultSet executeQuery() throws SQLException
{
return new StubResultSet();
}
/** {@inheritDoc} */
@Override
public int executeUpdate() throws SQLException
{
return 0;
}
/** {@inheritDoc} */
@Override
public void setNull(int parameterIndex, int sqlType) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBoolean(int parameterIndex, boolean x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setByte(int parameterIndex, byte x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setShort(int parameterIndex, short x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setInt(int parameterIndex, int x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setLong(int parameterIndex, long x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setFloat(int parameterIndex, float x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setDouble(int parameterIndex, double x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setString(int parameterIndex, String x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBytes(int parameterIndex, byte[] x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setDate(int parameterIndex, Date x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setTime(int parameterIndex, Time x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void clearParameters() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setObject(int parameterIndex, Object x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public boolean execute() throws SQLException
{
return false;
}
/** {@inheritDoc} */
@Override
public void addBatch() throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setRef(int parameterIndex, Ref x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBlob(int parameterIndex, Blob x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setClob(int parameterIndex, Clob x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setArray(int parameterIndex, Array x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public ResultSetMetaData getMetaData() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setURL(int parameterIndex, URL x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public ParameterMetaData getParameterMetaData() throws SQLException
{
return null;
}
/** {@inheritDoc} */
@Override
public void setRowId(int parameterIndex, RowId x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNString(int parameterIndex, String value) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNClob(int parameterIndex, NClob value) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setClob(int parameterIndex, Reader reader, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setClob(int parameterIndex, Reader reader) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException
{
}
/** {@inheritDoc} */
@Override
public void setNClob(int parameterIndex, Reader reader) throws SQLException
{
}

File diff suppressed because it is too large Load Diff

@ -37,6 +37,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
checkClosed();
@ -44,6 +45,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
checkClosed();
@ -51,6 +53,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public ResultSet executeQuery(String sql) throws SQLException
{
checkClosed();
@ -59,6 +62,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql) throws SQLException
{
checkClosed();
@ -66,12 +70,14 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void close() throws SQLException
{
closed = true;
}
/** {@inheritDoc} */
@Override
public int getMaxFieldSize() throws SQLException
{
checkClosed();
@ -79,12 +85,14 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void setMaxFieldSize(int max) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public int getMaxRows() throws SQLException
{
checkClosed();
@ -92,18 +100,21 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void setMaxRows(int max) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public void setEscapeProcessing(boolean enable) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public int getQueryTimeout() throws SQLException
{
checkClosed();
@ -111,18 +122,21 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void setQueryTimeout(int seconds) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public void cancel() throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public SQLWarning getWarnings() throws SQLException
{
checkClosed();
@ -130,18 +144,21 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void clearWarnings() throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public void setCursorName(String name) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql) throws SQLException
{
checkClosed();
@ -149,6 +166,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public ResultSet getResultSet() throws SQLException
{
checkClosed();
@ -156,6 +174,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int getUpdateCount() throws SQLException
{
checkClosed();
@ -163,6 +182,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean getMoreResults() throws SQLException
{
checkClosed();
@ -170,12 +190,14 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void setFetchDirection(int direction) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public int getFetchDirection() throws SQLException
{
checkClosed();
@ -183,12 +205,14 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void setFetchSize(int rows) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public int getFetchSize() throws SQLException
{
checkClosed();
@ -196,6 +220,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int getResultSetConcurrency() throws SQLException
{
checkClosed();
@ -203,6 +228,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int getResultSetType() throws SQLException
{
checkClosed();
@ -210,18 +236,21 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public void addBatch(String sql) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public void clearBatch() throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public int[] executeBatch() throws SQLException
{
checkClosed();
@ -229,6 +258,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public Connection getConnection() throws SQLException
{
checkClosed();
@ -236,6 +266,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean getMoreResults(int current) throws SQLException
{
checkClosed();
@ -243,6 +274,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public ResultSet getGeneratedKeys() throws SQLException
{
checkClosed();
@ -250,6 +282,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
{
checkClosed();
@ -257,6 +290,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
{
checkClosed();
@ -264,6 +298,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int executeUpdate(String sql, String[] columnNames) throws SQLException
{
checkClosed();
@ -271,6 +306,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
{
checkClosed();
@ -278,6 +314,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException
{
checkClosed();
@ -285,6 +322,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean execute(String sql, String[] columnNames) throws SQLException
{
checkClosed();
@ -292,6 +330,7 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public int getResultSetHoldability() throws SQLException
{
checkClosed();
@ -299,18 +338,21 @@ public class StubStatement implements Statement
}
/** {@inheritDoc} */
@Override
public boolean isClosed() throws SQLException
{
return closed;
}
/** {@inheritDoc} */
@Override
public void setPoolable(boolean poolable) throws SQLException
{
checkClosed();
}
/** {@inheritDoc} */
@Override
public boolean isPoolable() throws SQLException
{
checkClosed();

@ -1,4 +1,4 @@
acquireRetries=5
minimumIdle=5
connectionTestQuery=SELECT 1
autoCommit=false
dataSourceClassName=com.zaxxer.hikari.mocks.StubDataSource

@ -0,0 +1,6 @@
connectionTestQuery=SELECT 1
autoCommit=false
dataSourceClassName=com.zaxxer.hikari.mocks.StubDataSource
dataSource.user=test
dataSource.password=test
dataSource.url=jdbc:stub:foo
Loading…
Cancel
Save