diff --git a/CHANGES b/CHANGES index 556730a9..08b71b1f 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/pom.xml b/pom.xml index 68b09487..f53df426 100644 --- a/pom.xml +++ b/pom.xml @@ -83,6 +83,12 @@ 4.3.0.Final provided + + com.codahale.metrics + metrics-core + 3.0.2 + provided + @@ -148,7 +154,7 @@ HikariCP com.zaxxer.hikari - com.sun.tools.attach,javassist.*, + javassist.*, javax.management, javax.sql, javax.sql.rowset, @@ -199,7 +205,7 @@ 2.9.1 public - com.zaxxer.hikari.* + true 1024m diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index b063be73..ca14d7c7 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -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() diff --git a/src/main/java/com/zaxxer/hikari/HikariConfigMBean.java b/src/main/java/com/zaxxer/hikari/HikariConfigMBean.java index 543dcd46..d9384fd8 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfigMBean.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfigMBean.java @@ -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, diff --git a/src/main/java/com/zaxxer/hikari/HikariDataSource.java b/src/main/java/com/zaxxer/hikari/HikariDataSource.java index 2ac26288..747365df 100644 --- a/src/main/java/com/zaxxer/hikari/HikariDataSource.java +++ b/src/main/java/com/zaxxer/hikari/HikariDataSource.java @@ -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 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(); } /** @@ -59,24 +62,28 @@ public class HikariDataSource extends HikariConfig implements DataSource */ public HikariDataSource(HikariConfig configuration) { - super(); + configuration.validate(); configuration.copyState(this); + multiPool = new HashMap(); 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 unwrap(Class 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; + } + } } diff --git a/src/main/java/com/zaxxer/hikari/HikariPool.java b/src/main/java/com/zaxxer/hikari/HikariPool.java index 100e24b6..b0ebab40 100644 --- a/src/main/java/com/zaxxer/hikari/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/HikariPool.java @@ -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 idleConnectionBag; + private final ConcurrentBag 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(); - this.idleConnectionBag.addBagStateListener(this); - this.debug = LOGGER.isDebugEnabled(); + this.connectionBag = new ConcurrentBag(); + 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 list = idleConnectionBag.values(ConcurrentBag.STATE_NOT_IN_USE); + List 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 + } + } } } diff --git a/src/main/java/com/zaxxer/hikari/hibernate/HikariConfigurationUtil.java b/src/main/java/com/zaxxer/hikari/hibernate/HikariConfigurationUtil.java index bbaf1b2a..07690c1b 100644 --- a/src/main/java/com/zaxxer/hikari/hibernate/HikariConfigurationUtil.java +++ b/src/main/java/com/zaxxer/hikari/hibernate/HikariConfigurationUtil.java @@ -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") diff --git a/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java b/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java new file mode 100644 index 00000000..3bf21404 --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java @@ -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(); + } + } + } +} diff --git a/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java b/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java new file mode 100644 index 00000000..1dfb5ead --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java @@ -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) + { + } +} diff --git a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java index 5d7255b3..75647341 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java @@ -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 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 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 unwrap(Class iface) throws SQLException { diff --git a/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java b/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java index 43d35226..5504cb22 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java @@ -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); } diff --git a/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java b/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java index f878a2ef..07dd15f1 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java +++ b/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java @@ -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); diff --git a/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java b/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java index 4c63a6e0..c2215f47 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java +++ b/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java @@ -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; diff --git a/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java b/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java index e9ca61ab..33ae93f4 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/PreparedStatementProxy.java @@ -37,6 +37,7 @@ public abstract class PreparedStatementProxy extends StatementProxy implements P // ********************************************************************** /** {@inheritDoc} */ + @Override public final ResultSet executeQuery() throws SQLException { try diff --git a/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java b/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java index 06348822..d07f57e7 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java @@ -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; diff --git a/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java b/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java index a4e125a1..81a86154 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/StatementProxy.java @@ -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; diff --git a/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java b/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java index b3d5586c..e9d21d02 100644 --- a/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java +++ b/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java @@ -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>> threadList; @@ -127,7 +136,7 @@ public class ConcurrentBag(value)); + LinkedList> list = threadList.get(); + if (list == null) + { + list = new LinkedList>(); + threadList.set(list); + } + + list.addLast(new WeakReference(value)); synchronizer.releaseShared(returnTime); } else @@ -180,7 +196,7 @@ public class ConcurrentBag[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 unwrap(Class iface) throws SQLException + { + throw new SQLFeatureNotSupportedException(); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException + { + return false; + } + + public void shutdown() + { + } +} diff --git a/src/main/java/com/zaxxer/hikari/util/FastStatementList.java b/src/main/java/com/zaxxer/hikari/util/FastStatementList.java index 65eee2fa..fa0de408 100644 --- a/src/main/java/com/zaxxer/hikari/util/FastStatementList.java +++ b/src/main/java/com/zaxxer/hikari/util/FastStatementList.java @@ -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() { diff --git a/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java b/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java new file mode 100644 index 00000000..2c67fb43 --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java @@ -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 queue = new LinkedBlockingQueue(queueSize); + ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 10, TimeUnit.SECONDS, queue, threadFactory, new ThreadPoolExecutor.DiscardPolicy()); + executor.allowCoreThreadTimeOut(true); + return executor; + } +} diff --git a/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java b/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java index 035823e0..11e3fb25 100644 --- a/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java +++ b/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java @@ -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); diff --git a/src/test/java/com/zaxxer/hikari/ConnectionStateTest.java b/src/test/java/com/zaxxer/hikari/ConnectionStateTest.java index 1ded50f2..ba2b581c 100644 --- a/src/test/java/com/zaxxer/hikari/ConnectionStateTest.java +++ b/src/test/java/com/zaxxer/hikari/ConnectionStateTest.java @@ -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"); diff --git a/src/test/java/com/zaxxer/hikari/ExceptionTest.java b/src/test/java/com/zaxxer/hikari/ExceptionTest.java index bd714535..b34afc89 100644 --- a/src/test/java/com/zaxxer/hikari/ExceptionTest.java +++ b/src/test/java/com/zaxxer/hikari/ExceptionTest.java @@ -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"); diff --git a/src/test/java/com/zaxxer/hikari/JdbcDriverTest.java b/src/test/java/com/zaxxer/hikari/JdbcDriverTest.java new file mode 100644 index 00000000..6892d034 --- /dev/null +++ b/src/test/java/com/zaxxer/hikari/JdbcDriverTest.java @@ -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(); + } +} diff --git a/src/test/java/com/zaxxer/hikari/RampUpDown.java b/src/test/java/com/zaxxer/hikari/RampUpDown.java index 02fcc2fc..03cfbb33 100644 --- a/src/test/java/com/zaxxer/hikari/RampUpDown.java +++ b/src/test/java/com/zaxxer/hikari/RampUpDown.java @@ -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"); diff --git a/src/test/java/com/zaxxer/hikari/StatementTest.java b/src/test/java/com/zaxxer/hikari/StatementTest.java index 9a458403..d35ee984 100644 --- a/src/test/java/com/zaxxer/hikari/StatementTest.java +++ b/src/test/java/com/zaxxer/hikari/StatementTest.java @@ -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"); diff --git a/src/test/java/com/zaxxer/hikari/TestConnectionTimeoutRetry.java b/src/test/java/com/zaxxer/hikari/TestConnectionTimeoutRetry.java new file mode 100644 index 00000000..d1c97a98 --- /dev/null +++ b/src/test/java/com/zaxxer/hikari/TestConnectionTimeoutRetry.java @@ -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()); + } +} diff --git a/src/test/java/com/zaxxer/hikari/CreationTest.java b/src/test/java/com/zaxxer/hikari/TestConnections.java similarity index 79% rename from src/test/java/com/zaxxer/hikari/CreationTest.java rename to src/test/java/com/zaxxer/hikari/TestConnections.java index 309d698e..3f3e03dd 100644 --- a/src/test/java/com/zaxxer/hikari/CreationTest.java +++ b/src/test/java/com/zaxxer/hikari/TestConnections.java @@ -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()); } } diff --git a/src/test/java/com/zaxxer/hikari/TestPropertySetter.java b/src/test/java/com/zaxxer/hikari/TestPropertySetter.java index 0df672d3..41169866 100644 --- a/src/test/java/com/zaxxer/hikari/TestPropertySetter.java +++ b/src/test/java/com/zaxxer/hikari/TestPropertySetter.java @@ -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()); + } + } diff --git a/src/test/java/com/zaxxer/hikari/UnwrapTest.java b/src/test/java/com/zaxxer/hikari/UnwrapTest.java index 632c7b2f..914a9a09 100644 --- a/src/test/java/com/zaxxer/hikari/UnwrapTest.java +++ b/src/test/java/com/zaxxer/hikari/UnwrapTest.java @@ -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"); diff --git a/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java b/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java index 407f26be..02e1dcc3 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java +++ b/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java @@ -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 unwrap(Class 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() { + @Override public Void answer(InvocationOnMock invocation) throws Throwable { return null; diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java index cd7f8989..ee1d64eb 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java @@ -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); diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java index f020cb0c..c5845715 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java @@ -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 unwrap(Class 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> getTypeMap() throws SQLException { return null; } /** {@inheritDoc} */ + @Override public void setTypeMap(Map> 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; diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java b/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java index d20d3516..d7e7c936 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java @@ -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 unwrap(Class 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; + } } diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java b/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java index aff1bfae..aa453df3 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java @@ -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; diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java b/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java index 0046580f..5d6e91a3 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java @@ -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 unwrap(Class 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 { } diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java b/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java index 4cb05695..a4131489 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java @@ -48,6 +48,7 @@ public class StubResultSet implements ResultSet private boolean closed; /** {@inheritDoc} */ + @Override public T unwrap(Class iface) throws SQLException { @@ -55,1035 +56,1223 @@ public class StubResultSet implements ResultSet } /** {@inheritDoc} */ + @Override public boolean isWrapperFor(Class iface) throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean next() throws SQLException { return (counter > 100000); } /** {@inheritDoc} */ + @Override public void close() throws SQLException { closed = true; } /** {@inheritDoc} */ + @Override public boolean wasNull() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public String getString(int columnIndex) throws SQLException { return "aString"; } /** {@inheritDoc} */ + @Override public boolean getBoolean(int columnIndex) throws SQLException { return false; } /** {@inheritDoc} */ + @Override public byte getByte(int columnIndex) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public short getShort(int columnIndex) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public int getInt(int columnIndex) throws SQLException { return ++counter; } /** {@inheritDoc} */ + @Override public long getLong(int columnIndex) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public float getFloat(int columnIndex) throws SQLException { throw new SQLException("No reason", "08999"); } /** {@inheritDoc} */ + @Override public double getDouble(int columnIndex) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public byte[] getBytes(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Date getDate(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Time getTime(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Timestamp getTimestamp(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public InputStream getAsciiStream(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public InputStream getUnicodeStream(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public InputStream getBinaryStream(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public String getString(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public boolean getBoolean(String columnLabel) throws SQLException { return false; } /** {@inheritDoc} */ + @Override public byte getByte(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public short getShort(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public int getInt(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public long getLong(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public float getFloat(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public double getDouble(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public byte[] getBytes(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Date getDate(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Time getTime(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Timestamp getTimestamp(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public InputStream getAsciiStream(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public InputStream getUnicodeStream(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public InputStream getBinaryStream(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public SQLWarning getWarnings() throws SQLException { return null; } /** {@inheritDoc} */ + @Override public void clearWarnings() throws SQLException { } /** {@inheritDoc} */ + @Override public String getCursorName() throws SQLException { return null; } /** {@inheritDoc} */ + @Override public ResultSetMetaData getMetaData() throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Object getObject(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Object getObject(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public int findColumn(String columnLabel) throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public Reader getCharacterStream(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Reader getCharacterStream(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public BigDecimal getBigDecimal(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public boolean isBeforeFirst() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean isAfterLast() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean isFirst() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean isLast() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public void beforeFirst() throws SQLException { } /** {@inheritDoc} */ + @Override public void afterLast() throws SQLException { } /** {@inheritDoc} */ + @Override public boolean first() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean last() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public int getRow() throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public boolean absolute(int row) throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean relative(int rows) throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean previous() throws SQLException { return false; } /** {@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 getType() throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public int getConcurrency() throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public boolean rowUpdated() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean rowInserted() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public boolean rowDeleted() throws SQLException { return false; } /** {@inheritDoc} */ + @Override public void updateNull(int columnIndex) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBoolean(int columnIndex, boolean x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateByte(int columnIndex, byte x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateShort(int columnIndex, short x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateInt(int columnIndex, int x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateLong(int columnIndex, long x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateFloat(int columnIndex, float x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateDouble(int columnIndex, double x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateString(int columnIndex, String x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBytes(int columnIndex, byte[] x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateDate(int columnIndex, Date x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateTime(int columnIndex, Time x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateObject(int columnIndex, Object x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNull(String columnLabel) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBoolean(String columnLabel, boolean x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateByte(String columnLabel, byte x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateShort(String columnLabel, short x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateInt(String columnLabel, int x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateLong(String columnLabel, long x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateFloat(String columnLabel, float x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateDouble(String columnLabel, double x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateString(String columnLabel, String x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBytes(String columnLabel, byte[] x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateDate(String columnLabel, Date x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateTime(String columnLabel, Time x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateObject(String columnLabel, Object x) throws SQLException { } /** {@inheritDoc} */ + @Override public void insertRow() throws SQLException { } /** {@inheritDoc} */ + @Override public void updateRow() throws SQLException { } /** {@inheritDoc} */ + @Override public void deleteRow() throws SQLException { } /** {@inheritDoc} */ + @Override public void refreshRow() throws SQLException { } /** {@inheritDoc} */ + @Override public void cancelRowUpdates() throws SQLException { } /** {@inheritDoc} */ + @Override public void moveToInsertRow() throws SQLException { } /** {@inheritDoc} */ + @Override public void moveToCurrentRow() throws SQLException { } /** {@inheritDoc} */ + @Override public Statement getStatement() throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Object getObject(int columnIndex, Map> map) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Ref getRef(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Blob getBlob(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Clob getClob(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Array getArray(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Object getObject(String columnLabel, Map> map) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Ref getRef(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Blob getBlob(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Clob getClob(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Array getArray(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Date getDate(String columnLabel, Calendar cal) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Time getTime(int columnIndex, Calendar cal) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Time getTime(String columnLabel, Calendar cal) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public URL getURL(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public URL getURL(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public void updateRef(int columnIndex, Ref x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateRef(String columnLabel, Ref x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBlob(int columnIndex, Blob x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBlob(String columnLabel, Blob x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateClob(int columnIndex, Clob x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateClob(String columnLabel, Clob x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateArray(int columnIndex, Array x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateArray(String columnLabel, Array x) throws SQLException { } /** {@inheritDoc} */ + @Override public RowId getRowId(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public RowId getRowId(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public void updateRowId(int columnIndex, RowId x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateRowId(String columnLabel, RowId x) throws SQLException { } /** {@inheritDoc} */ + @Override public int getHoldability() throws SQLException { return 0; } /** {@inheritDoc} */ + @Override public boolean isClosed() throws SQLException { return closed; } /** {@inheritDoc} */ + @Override public void updateNString(int columnIndex, String nString) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNString(String columnLabel, String nString) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNClob(int columnIndex, NClob nClob) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNClob(String columnLabel, NClob nClob) throws SQLException { } /** {@inheritDoc} */ + @Override public NClob getNClob(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public NClob getNClob(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public SQLXML getSQLXML(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public SQLXML getSQLXML(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { } /** {@inheritDoc} */ + @Override public String getNString(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public String getNString(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Reader getNCharacterStream(int columnIndex) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public Reader getNCharacterStream(String columnLabel) throws SQLException { return null; } /** {@inheritDoc} */ + @Override public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateClob(int columnIndex, Reader reader) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateClob(String columnLabel, Reader reader) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNClob(int columnIndex, Reader reader) throws SQLException { } /** {@inheritDoc} */ + @Override public void updateNClob(String columnLabel, Reader reader) throws SQLException { } diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java b/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java index 5db6c618..335bb366 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java @@ -37,6 +37,7 @@ public class StubStatement implements Statement } /** {@inheritDoc} */ + @Override public T unwrap(Class 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(); diff --git a/src/test/resources/propfile1.properties b/src/test/resources/propfile1.properties index 15f1711a..bf00bdde 100644 --- a/src/test/resources/propfile1.properties +++ b/src/test/resources/propfile1.properties @@ -1,4 +1,4 @@ -acquireRetries=5 +minimumIdle=5 connectionTestQuery=SELECT 1 autoCommit=false dataSourceClassName=com.zaxxer.hikari.mocks.StubDataSource diff --git a/src/test/resources/propfile3.properties b/src/test/resources/propfile3.properties new file mode 100644 index 00000000..10fdcf1f --- /dev/null +++ b/src/test/resources/propfile3.properties @@ -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