Merge branch 'master' into dev

* master: (40 commits)
  Fix test breakage, plus some minor cleanup.
  Remove multi-pool (DataSource.getConnection(username, password) support.
  Exclude PoolInitializationException from coverage
  Update README.md
  Added jacoco to pom
  Added profile for coveralls
  Update .travis.yml
  Change travis-ci configuration for master
  More improved logging.
  If the pool is maxed out, return 'true' from addConnection() so we stop trying to add the connection.
  Fix #258 Improve logging of closed connection reason
  Recombine into a single Java 7 compatible build.
  Update README.md
  Fix #288 allow explicit definition of driverClassName to override DriverManager.getDriver(url) located driver.
  Mark scope as 'test'
  Update README.md
  Update CHANGES
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release HikariCP-2.3.5
  Fix #286 fix regression in Dropwizard runtime dependency
  ...

Conflicts:
	.travis.yml
	README.md
	hikaricp-common/pom.xml
	hikaricp-common/src/main/java/com/zaxxer/hikari/AbstractHikariConfig.java
	hikaricp-common/src/main/java/com/zaxxer/hikari/HikariConfig.java
	hikaricp-common/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java
	hikaricp-java6/src/test/java/com/zaxxer/hikari/TestMetrics.java
	hikaricp-java7/pom.xml
	hikaricp-java7/src/main/java/com/zaxxer/hikari/pool/HikariPool.java
	hikaricp-java7/src/main/java/com/zaxxer/hikari/util/Java7ConcurrentBag.java
	hikaricp-java7/src/main/resources/META-INF/codex.properties
	hikaricp-java7/src/test/java/com/zaxxer/hikari/TestMetrics.java
	hikaricp-java7/src/test/java/com/zaxxer/hikari/osgi/OSGiBundleTest.java
	hikaricp-java7/src/test/resources/hibernate.properties
	hikaricp-java7/src/test/resources/propfile1.properties
	hikaricp-java7/src/test/resources/propfile2.properties
	hikaricp-java7/src/test/resources/propfile3.properties
	hikaricp/pom.xml
	hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java
	hikaricp/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java
	hikaricp/src/main/java/com/zaxxer/hikari/util/Java8ConcurrentBag.java
	pom.xml
	src/main/java/com/zaxxer/hikari/HikariConfig.java
	src/main/java/com/zaxxer/hikari/pool/HikariPool.java
	src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java
	src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java
	src/test/java/com/zaxxer/hikari/TestConcurrentBag.java
	src/test/java/com/zaxxer/hikari/TestMetrics.java
pull/295/head
Brett Wooldridge 10 years ago
commit 37d5cc722e

@ -6,6 +6,4 @@ install: /bin/true
script:
- mvn install -Dskip.unit.tests=true -Dmaven.javadoc.skip=true -V -B
- mvn clean test coveralls:report -V -pl hikaricp -P coverage
- jdk_switcher use openjdk7
- mvn test -V -pl hikaricp-java7
- mvn clean test coveralls:report -V -P coverage

@ -1,5 +1,39 @@
HikariCP Changes
Changes in 2.3.5
* Fixed regression caused by enhancement #279 that imposed a runtime dependency on
Dropwizard metrics.
Changes in 2.3.4
* Fixed class cast exception when setting the HealthCheckRegistry via JNDI lookup.
* Allow Dropwizard MetricRegistry/HealthCheckRegistry to be set after pool startup --
one time only.
* Make logger in BaseHikariPool non-static and use getClass() to log messages as the
implementation class rather than as BaseHikariPool.
* Removed deprecation from connectionInitSql, it will be allowed.
* Made suspect/resume lock non-static (should be be shared across pools).
* Improved unwrap() behavior in the Hibernate HikariConnectionProvider.
* Improved leak detection log
Changes in 2.3.3
* Fixed bad interaction with PostgeSQL JDBC driver whereby a SQLException thrown by
PostgreSQL where the getNextException() call returns the original exception and causes
an infinite loop in HikariCP (and eventual stack overflow).
* Throw a typed Exception rather than a simple RuntimeException when pool initialization
fails.
* Allow Dropwizard Metrics and HealthChecks to be configured by a JNDI lookup.
Changes in 2.3.2
* Add support for Dropwizard HealthChecks through the introduction of two initial health

@ -1,4 +1,4 @@
![](https://github.com/brettwooldridge/HikariCP/wiki/Hikari.png) HikariCP <sup><sup>It's Faster.&nbsp;</sup></sup>[![Travis branch](https://img.shields.io/travis/joyent/node/v0.6.svg)](https://travis-ci.org/brettwooldridge/HikariCP?branch=dev)<img src='https://raw.github.com/wiki/brettwooldridge/HikariCP/space60x1.gif' width='3px'>[![Issue Stats](http://issuestats.com/github/brettwooldridge/HikariCP/badge/issue)](http://issuestats.com/github/brettwooldridge/HikariCP)<img src='https://raw.github.com/wiki/brettwooldridge/HikariCP/space60x1.gif' width='3px'>[![Coverage Status](https://img.shields.io/coveralls/brettwooldridge/HikariCP/dev.svg)](https://coveralls.io/r/brettwooldridge/HikariCP?branch=dev)<br><sub><sub><sup>Hi·ka·ri [hi·ka·'lē] &#40;*Origin: Japanese*): light; ray.</sup></sub></sub>
![](https://github.com/brettwooldridge/HikariCP/wiki/Hikari.png) HikariCP <sup><sup>It's Faster.&nbsp;</sup></sup>[![Travis branch](https://img.shields.io/travis/joyent/node/v0.6.svg)](https://travis-ci.org/brettwooldridge/HikariCP)<img src='https://raw.github.com/wiki/brettwooldridge/HikariCP/space60x1.gif' width='3px'>[![Issue Stats](http://issuestats.com/github/brettwooldridge/HikariCP/badge/issue?style=flat)](http://issuestats.com/github/brettwooldridge/HikariCP)<img src='https://raw.github.com/wiki/brettwooldridge/HikariCP/space60x1.gif' width='3px'>[![Coverage Status](https://img.shields.io/coveralls/brettwooldridge/HikariCP/master.svg)](https://coveralls.io/r/brettwooldridge/HikariCP?branch=master)<br><sub><sub><sup>Hi·ka·ri [hi·ka·'lē] &#40;*Origin: Japanese*): light; ray.</sup></sub></sub>
==========
Fast, simple, reliable. HikariCP is a "zero-overhead" production ready JDBC connection pool. Coming in at roughly 70Kb, the library is very light. Read about [how we do it here](https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole).
@ -13,25 +13,16 @@ _Java 8 maven artifact:_
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.4.0</version>
<version>2.3.5</version>
<scope>compile</scope>
</dependency>
```
_Java 7 maven artifact:_
```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java7</artifactId>
<version>2.4.0</version>
<scope>compile</scope>
</dependency>
```
_Java 6 maven artifact:_
_Java 6 and Java 7 maven artifact:_
```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java6</artifactId>
<version>2.3.2</version>
<version>2.3.5</version>
<scope>compile</scope>
</dependency>
```

@ -1,41 +0,0 @@
/*
* Copyright (C) 2013 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;
/**
* Interface whose implementers can perform one-time customization of a
* Connection before it is added to the pool. Note the implemention
* of the <code>customize()</code> method must be multithread-safe as
* it may be called by multiple threads at one time.
*
* @author Brett Wooldridge
*/
public interface IConnectionCustomizer
{
/**
* The Connection object that is passed into this method is the "raw"
* Connection instance provided by the JDBC driver, not a wrapped
* HikariCP connection.
*
* @param connection a native JDBC driver Connection instance to customize
* @throws SQLException should be thrown if an error condition is encountered during customization
*/
void customize(Connection connection) throws SQLException;
}

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-parent</artifactId>
<artifactId>HikariCP</artifactId>
<version>2.4.0-SNAPSHOT</version>
<packaging>pom</packaging>
<packaging>jar</packaging>
<name>HikariCP-parent</name>
<name>HikariCP</name>
<description>Ultimate JDBC Connection Pool</description>
<url>https://github.com/brettwooldridge/HikariCP</url>
@ -40,9 +39,17 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<felix.version>4.2.1</felix.version>
<felix.version>4.6.0</felix.version>
<hibernate.version>4.3.8.Final</hibernate.version>
<javassist.version>3.19.0-GA</javassist.version>
<jndi.version>0.11.4.1</jndi.version>
<metrics.version>3.1.0</metrics.version>
<mockito.version>1.10.19</mockito.version>
<pax.exam.version>3.4.0</pax.exam.version>
<pax.url.version>1.6.0</pax.url.version>
<pax.url.version>2.3.0</pax.url.version>
<slf4j.version>1.7.10</slf4j.version>
<felix.bundle.plugin.version>2.4.0</felix.bundle.plugin.version>
<felix.version>4.2.1</felix.version>
</properties>
<parent>
@ -51,64 +58,65 @@
<version>7</version>
</parent>
<modules>
<module>hikaricp</module>
<module>hikaricp-java7</module>
</modules>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
<version>${slf4j.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.19.0-GA</version>
<version>${javassist.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.0.Final</version>
<version>${hibernate.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.1.0</version>
<version>${metrics.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-healthchecks</artifactId>
<version>3.1.0</version>
<version>${metrics.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>simple-jndi</groupId>
<artifactId>simple-jndi</artifactId>
<version>0.11.4.1</version>
<version>${jndi.version}</version>
<scope>test</scope>
</dependency>
<!-- OSGi test -->
<!-- OSGi test -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
@ -148,8 +156,124 @@
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.4.201502262128</version>
<executions>
<!-- Prepares the property pointing to the JaCoCo runtime agent which is passed as VM argument when Maven the Surefire plugin is executed. -->
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<destFile>${project.build.directory}/coverage-reports/jacoco.exec</destFile>
<!-- Sets the name of the property containing the settings for JaCoCo runtime agent. -->
<propertyName>surefireArgLine</propertyName>
<excludes>
<exclude>**/com/zaxxer/hikari/proxy/ProxyFactory</exclude>
<exclude>**/com/zaxxer/hikari/proxy/ResultSetProxy</exclude>
<exclude>**/com/zaxxer/hikari/proxy/StatementProxy</exclude>
<exclude>**/com/zaxxer/hikari/proxy/CallableStatementProxy</exclude>
<exclude>**/com/zaxxer/hikari/proxy/PreparedStatementProxy</exclude>
<exclude>**/com/zaxxer/hikari/pool/PoolInitializationException</exclude>
<exclude>**/com/zaxxer/hikari/metrics/**</exclude>
</excludes>
</configuration>
</execution>
<!-- Ensures that the code coverage report for unit tests is created after unit tests have been run. -->
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/coverage-reports/jacoco.exec</dataFile>
<!-- Sets the output directory for the code coverage report. -->
<outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
<excludes>
<exclude>**/com/zaxxer/hikari/proxy/ProxyFactory.class</exclude>
<exclude>**/com/zaxxer/hikari/proxy/ResultSetProxy.class</exclude>
<exclude>**/com/zaxxer/hikari/proxy/StatementProxy.class</exclude>
<exclude>**/com/zaxxer/hikari/proxy/CallableStatementProxy.class</exclude>
<exclude>**/com/zaxxer/hikari/proxy/PreparedStatementProxy.class</exclude>
<exclude>**/com/zaxxer/hikari/pool/PoolInitializationException.class</exclude>
<exclude>**/com/zaxxer/hikari/metrics/**</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${felix.bundle.plugin.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Name>HikariCP</Bundle-Name>
<Export-Package>
com.zaxxer.hikari,
com.zaxxer.hikari.hibernate
</Export-Package>
<Private-Package>com.zaxxer.hikari.*</Private-Package>
<_exportcontents>
com.zaxxer.hikari.pool,
com.zaxxer.hikari.util,
com.zaxxer.hikari.proxy
</_exportcontents>
<Import-Package>
javassist.*,
javax.management,
javax.naming,
javax.naming.spi,
javax.sql,
javax.sql.rowset,
javax.sql.rowset.serial,
javax.sql.rowset.spi,
com.codahale.metrics;resolution:=optional,
com.codahale.metrics.health;resolution:=optional,
org.slf4j;version="[1.6,2)",
org.hibernate;resolution:=optional,
org.hibernate.cfg;resolution:=optional,
org.hibernate.engine.jdbc.connections.spi;resolution:=optional,
org.hibernate.service;resolution:=optional,
org.hibernate.service.spi;resolution:=optional
</Import-Package>
<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
<executions>
<!-- This execution makes sure that the manifest is available when the tests are executed -->
<execution>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<extensions>true</extensions>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
@ -165,9 +289,9 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>2.15</version>
<configuration>
<!-- Sets the VM argument line used when unit tests are run. -->
<!-- Sets the VM argument line used when unit tests are run. -->
<argLine>${surefireArgLine}</argLine>
<!-- Skips unit tests if the value of skip.unit.tests property is true -->
<!-- Skips unit tests if the value of skip.unit.tests property is true -->
<skipTests>${skip.unit.tests}</skipTests>
</configuration>
</plugin>
@ -177,7 +301,8 @@
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<!-- outputDirectory>/absolute/path/to/the/output/directory</outputDirectory> <finalName>filename-of-generated-jar-file</finalName -->
<!-- outputDirectory>/absolute/path/to/the/output/directory</outputDirectory>
<finalName>filename-of-generated-jar-file</finalName -->
<attach>true</attach>
</configuration>
<executions>
@ -196,10 +321,9 @@
<version>2.9.1</version>
<configuration>
<show>public</show>
<!-- excludePackageNames>com.zaxxer.hikari.*</excludePackageNames -->
<!-- excludePackageNames>com.zaxxer.hikari.*</excludePackageNames -->
<attach>true</attach>
<maxmemory>1024m</maxmemory>
<sourcepath>../hikaricp-common/src/main/java</sourcepath>
</configuration>
<executions>
<execution>
@ -217,6 +341,29 @@
</build>
<profiles>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>coveralls</id>
<phase>verify</phase>
<goals>
<goal>jacoco</goal>
</goals>
<inherited>false</inherited>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
@ -244,5 +391,28 @@
</plugins>
</build>
</profile>
<profile>
<id>felix</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>pax.exam.framework</name>
<value>felix</value>
</property>
</activation>
<properties>
<pax.exam.framework>felix</pax.exam.framework>
<pax.exam.logging>none</pax.exam.logging>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>${felix.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

@ -0,0 +1,841 @@
/*
* 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;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.proxy.JavassistProxyFactory;
import com.zaxxer.hikari.util.PropertyBeanSetter;
import com.zaxxer.hikari.util.UtilityElf;
public class HikariConfig implements HikariConfigMBean
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariConfig.class);
private static final long CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10);
private static final long MAX_LIFETIME = TimeUnit.MINUTES.toMillis(30);
private static int poolNumber;
private static boolean unitTest;
// Properties changeable at runtime through the MBean
//
private volatile long connectionTimeout;
private volatile long validationTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
// Properties NOT changeable at runtime
//
private String catalog;
private String connectionCustomizerClassName;
private String connectionInitSql;
private String connectionTestQuery;
private String dataSourceClassName;
private String dataSourceJndiName;
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 isIsolateInternalQueries;
private boolean isRegisterMbeans;
private boolean isAllowPoolSuspension;
private DataSource dataSource;
private Properties dataSourceProperties;
private ThreadFactory threadFactory;
private Object metricRegistry;
private Object healthCheckRegistry;
private Properties healthCheckProperties;
static
{
JavassistProxyFactory.initialize();
}
/**
* Default constructor
*/
public HikariConfig()
{
dataSourceProperties = new Properties();
healthCheckProperties = new Properties();
connectionTimeout = CONNECTION_TIMEOUT;
validationTimeout = VALIDATION_TIMEOUT;
idleTimeout = IDLE_TIMEOUT;
isAutoCommit = true;
isInitializationFailFast = true;
minIdle = -1;
maxPoolSize = 10;
maxLifetime = MAX_LIFETIME;
String systemProp = System.getProperty("hikaricp.configurationFile");
if ( systemProp != null) {
loadProperties(systemProp);
}
}
/**
* Construct a HikariConfig from the specified properties object.
*
* @param properties the name of the property file
*/
public HikariConfig(Properties properties)
{
this();
PropertyBeanSetter.setTargetFromProperties(this, properties);
}
/**
* Construct a HikariConfig from the specified property file name. <code>propertyFileName</code>
* will first be treated as a path in the file-system, and if that fails the
* ClassLoader.getResourceAsStream(propertyFileName) will be tried.
*
* @param propertyFileName the name of the property file
*/
public HikariConfig(String propertyFileName)
{
this();
loadProperties(propertyFileName);
}
/**
* 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 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 <code>Connection.isValid()</code> 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) {
this.connectionTimeout = Integer.MAX_VALUE;
}
else if (connectionTimeoutMs < 1000) {
throw new IllegalArgumentException("connectionTimeout cannot be less than 1000ms");
}
else {
this.connectionTimeout = connectionTimeoutMs;
}
}
/** {@inheritDoc} */
@Override
public long getValidationTimeout()
{
return validationTimeout;
}
/** {@inheritDoc} */
@Override
public void setValidationTimeout(long validationTimeoutMs)
{
if (validationTimeoutMs < 1000) {
throw new IllegalArgumentException("validationTimeout cannot be less than 1000ms");
}
else {
this.validationTimeout = validationTimeoutMs;
}
}
/**
* 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;
}
public String getDataSourceClassName()
{
return dataSourceClassName;
}
public void setDataSourceClassName(String className)
{
this.dataSourceClassName = className;
}
public void addDataSourceProperty(String propertyName, Object value)
{
dataSourceProperties.put(propertyName, value);
}
public String getDataSourceJNDI()
{
return this.dataSourceJndiName;
}
public void setDataSourceJNDI(String jndiDataSource)
{
this.dataSourceJndiName = jndiDataSource;
}
public Properties getDataSourceProperties()
{
return dataSourceProperties;
}
public void setDataSourceProperties(Properties dsProperties)
{
dataSourceProperties.putAll(dsProperties);
}
public String getDriverClassName()
{
return driverClassName;
}
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)
{
if (idleTimeoutMs < 0) {
throw new IllegalArgumentException("idleTimeout cannot be negative");
}
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 the pool suspension behavior (allowed or disallowed).
*
* @return the pool suspension behavior
*/
public boolean isAllowPoolSuspension()
{
return isAllowPoolSuspension;
}
/**
* Set whether or not pool suspension is allowed. There is a performance
* impact when pool suspension is enabled. Unless you need it (for a
* redundancy system for example) do not enable it.
*
* @param isAllowPoolSuspension the desired pool suspension allowance
*/
public void setAllowPoolSuspension(boolean isAllowPoolSuspension)
{
this.isAllowPoolSuspension = isAllowPoolSuspension;
}
/**
* 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;
}
@Deprecated
public boolean isJdbc4ConnectionTest()
{
return false;
}
@Deprecated
public void setJdbc4ConnectionTest(boolean useIsValid)
{
// ignored deprecated property
LOGGER.warn("The jdbcConnectionTest property is now deprecated, see the documentation for connectionTestQuery");
}
/**
* Get the Codahale MetricRegistry, could be null.
*
* @return the codahale MetricRegistry instance
*/
public Object getMetricRegistry()
{
return metricRegistry;
}
/**
* Set a Codahale MetricRegistry to use for HikariCP.
*
* @param metricRegistry the Codahale MetricRegistry to set
*/
public void setMetricRegistry(Object metricRegistry)
{
if (metricRegistry != null) {
if (metricRegistry instanceof String) {
try {
InitialContext initCtx = new InitialContext();
metricRegistry = (MetricRegistry) initCtx.lookup((String) metricRegistry);
}
catch (NamingException e) {
throw new IllegalArgumentException(e);
}
}
if (!(metricRegistry instanceof MetricRegistry)) {
throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.MetricRegistry");
}
}
this.metricRegistry = metricRegistry;
}
/**
* Get the Codahale HealthCheckRegistry, could be null.
*
* @return the Codahale HealthCheckRegistry instance
*/
public Object getHealthCheckRegistry()
{
return healthCheckRegistry;
}
/**
* Set a Codahale HealthCheckRegistry to use for HikariCP.
*
* @param healthCheckRegistry the Codahale HealthCheckRegistry to set
*/
public void setHealthCheckRegistry(Object healthCheckRegistry)
{
if (healthCheckRegistry != null) {
if (healthCheckRegistry instanceof String) {
try {
InitialContext initCtx = new InitialContext();
healthCheckRegistry = (HealthCheckRegistry) initCtx.lookup((String) healthCheckRegistry);
}
catch (NamingException e) {
throw new IllegalArgumentException(e);
}
}
if (!(healthCheckRegistry instanceof HealthCheckRegistry)) {
throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.health.HealthCheckRegistry");
}
}
this.healthCheckRegistry = healthCheckRegistry;
}
public Properties getHealthCheckProperties()
{
return healthCheckProperties;
}
public void setHealthCheckProperties(Properties healthCheckProperties)
{
this.healthCheckProperties.putAll(healthCheckProperties);
}
public void addHealthCheckProperty(String key, String value)
{
healthCheckProperties.setProperty(key, value);
}
public boolean isReadOnly()
{
return isReadOnly;
}
public void setReadOnly(boolean readOnly)
{
this.isReadOnly = readOnly;
}
public boolean isRegisterMbeans()
{
return isRegisterMbeans;
}
public void setRegisterMbeans(boolean register)
{
this.isRegisterMbeans = register;
}
/** {@inheritDoc} */
@Override
public long getLeakDetectionThreshold()
{
return leakDetectionThreshold;
}
/** {@inheritDoc} */
@Override
public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
{
this.leakDetectionThreshold = leakDetectionThresholdMs;
}
/** {@inheritDoc} */
@Override
public long getMaxLifetime()
{
return maxLifetime;
}
/** {@inheritDoc} */
@Override
public void setMaxLifetime(long maxLifetimeMs)
{
this.maxLifetime = maxLifetimeMs;
}
/** {@inheritDoc} */
@Override
public int getMaximumPoolSize()
{
return maxPoolSize;
}
/** {@inheritDoc} */
@Override
public void setMaximumPoolSize(int maxPoolSize)
{
if (maxPoolSize < 1) {
throw new IllegalArgumentException("maxPoolSize cannot be less than 1");
}
this.maxPoolSize = maxPoolSize;
}
/** {@inheritDoc} */
@Override
public int getMinimumIdle()
{
return minIdle;
}
/** {@inheritDoc} */
@Override
public void setMinimumIdle(int minIdle)
{
if (minIdle < 0) {
throw new IllegalArgumentException("minimumIdle cannot be negative");
}
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;
}
/**
* Set the name of the connection pool. This is primarily used for the MBean
* to uniquely identify the pool configuration.
*
* @param poolName the name of the connection pool to use
*/
public void setPoolName(String poolName)
{
this.poolName = poolName;
}
public String getTransactionIsolation()
{
return transactionIsolationName;
}
/**
* Set the default transaction isolation level. The specified value is the
* constant name from the <code>Connection</code> class, eg.
* <code>TRANSACTION_REPEATABLE_READ</code>.
*
* @param isolationLevel the name of the isolation level
*/
public void setTransactionIsolation(String isolationLevel)
{
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;
}
/**
* Get the thread factory used to create threads.
*
* @return the thread factory (may be null, in which case the default thread factory is used)
*/
public ThreadFactory getThreadFactory()
{
return threadFactory;
}
/**
* Set the thread factory to be used to create threads.
*
* @param threadFactory the thread factory (setting to null causes the default thread factory to be used)
*/
public void setThreadFactory(ThreadFactory threadFactory)
{
this.threadFactory = threadFactory;
}
public void validate()
{
Logger logger = LoggerFactory.getLogger(getClass());
validateNumerics();
if (connectionCustomizerClassName != null) {
try {
getClass().getClassLoader().loadClass(connectionCustomizerClassName);
}
catch (Exception e) {
logger.warn("connectionCustomizationClass specified class '" + connectionCustomizerClassName + "' could not be loaded", e);
connectionCustomizerClassName = null;
}
}
if (driverClassName != null && jdbcUrl == null) {
logger.error("when specifying driverClassName, jdbcUrl must also be specified");
throw new IllegalStateException("when specifying driverClassName, jdbcUrl must also be specified");
}
else if (driverClassName != null && dataSourceClassName != null) {
logger.error("both driverClassName and dataSourceClassName are specified, one or the other should be used");
throw new IllegalStateException("both driverClassName and dataSourceClassName are specified, one or the other should be used");
}
else if (jdbcUrl != null) {
// 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 (transactionIsolationName != null) {
UtilityElf.getTransactionIsolation(transactionIsolationName);
}
if (poolName == null) {
poolName = "HikariPool-" + poolNumber++;
}
if (LOGGER.isDebugEnabled() || unitTest) {
logConfiguration();
}
}
private void validateNumerics()
{
Logger logger = LoggerFactory.getLogger(getClass());
if (validationTimeout > connectionTimeout && connectionTimeout != 0) {
logger.warn("validationTimeout is greater than connectionTimeout, setting validationTimeout to connectionTimeout.");
validationTimeout = connectionTimeout;
}
if (minIdle < 0 || minIdle > maxPoolSize) {
minIdle = maxPoolSize;
}
if (maxLifetime < 0) {
logger.error("maxLifetime cannot be negative.");
throw new IllegalArgumentException("maxLifetime cannot be negative.");
}
else if (maxLifetime > 0 && maxLifetime < TimeUnit.SECONDS.toMillis(30)) {
logger.warn("maxLifetime is less than 30000ms, using default {}ms.", MAX_LIFETIME);
maxLifetime = MAX_LIFETIME;
}
if (idleTimeout != 0 && idleTimeout < TimeUnit.SECONDS.toMillis(10)) {
logger.warn("idleTimeout is less than 10000ms, using default {}ms.", IDLE_TIMEOUT);
idleTimeout = IDLE_TIMEOUT;
}
else if (idleTimeout > maxLifetime && maxLifetime > 0) {
logger.warn("idleTimeout is greater than maxLifetime, setting to maxLifetime.");
idleTimeout = maxLifetime;
}
if (leakDetectionThreshold != 0 && leakDetectionThreshold < TimeUnit.SECONDS.toMillis(2) && !unitTest) {
logger.warn("leakDetectionThreshold is less than 2000ms, setting to minimum 2000ms.");
leakDetectionThreshold = 2000L;
}
}
private void logConfiguration()
{
LOGGER.debug("HikariCP pool {} configuration:", poolName);
final Set<String> propertyNames = new TreeSet<String>(PropertyBeanSetter.getPropertyNames(HikariConfig.class));
for (String prop : propertyNames) {
try {
Object value = PropertyBeanSetter.getProperty(prop, this);
if ("dataSourceProperties".equals(prop)) {
Properties dsProps = PropertyBeanSetter.copyProperties(dataSourceProperties);
dsProps.setProperty("password", "<masked>");
value = dsProps;
}
value = (prop.contains("password") ? "<masked>" : value);
LOGGER.debug((prop + "................................................").substring(0, 32) + (value != null ? value : ""));
}
catch (Exception e) {
continue;
}
}
}
protected void loadProperties(String propertyFileName)
{
final File propFile = new File(propertyFileName);
try (final InputStream is = propFile.isFile() ? new FileInputStream(propFile) : this.getClass().getResourceAsStream(propertyFileName)) {
if (is != null) {
Properties props = new Properties();
props.load(is);
PropertyBeanSetter.setTargetFromProperties(this, props);
}
else {
throw new IllegalArgumentException("Property file " + propertyFileName + " was not found.");
}
}
catch (IOException io) {
throw new RuntimeException("Error loading properties file", io);
}
}
public void copyState(HikariConfig other)
{
for (Field field : HikariConfig.class.getDeclaredFields()) {
if (!Modifier.isFinal(field.getModifiers())) {
field.setAccessible(true);
try {
field.set(other, field.get(this));
}
catch (Exception e) {
throw new RuntimeException("Exception copying HikariConfig state: " + e.getMessage(), e);
}
}
}
}
}

@ -22,13 +22,14 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Wrapper;
import java.util.HashMap;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.util.DriverDataSource;
@ -42,9 +43,6 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class);
// We use a concrete HashMap rather than Map to avoid an invokeinterface callsite
private final HashMap<MultiPoolKey, HikariPool> multiPool;
private volatile boolean isShutdown;
private final HikariPool fastPathPool;
@ -60,7 +58,6 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
{
super();
fastPathPool = null;
multiPool = new HashMap<MultiPoolKey, HikariPool>();
}
/**
@ -72,11 +69,9 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
{
configuration.validate();
configuration.copyState(this);
multiPool = new HashMap<MultiPoolKey, HikariPool>();
LOGGER.info("HikariCP pool {} is starting.", configuration.getPoolName());
pool = fastPathPool = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
}
/** {@inheritDoc} */
@ -100,7 +95,6 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
validate();
LOGGER.info("HikariCP pool {} is starting.", getPoolName());
pool = result = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
}
}
}
@ -110,25 +104,9 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
/** {@inheritDoc} */
@Override
@Deprecated
public Connection getConnection(String username, String password) throws SQLException
{
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 hikariPool.getConnection();
throw new SQLFeatureNotSupportedException();
}
/** {@inheritDoc} */
@ -151,18 +129,15 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
for (HikariPool hikariPool : multiPool.values()) {
hikariPool.getDataSource().setLoginTimeout(seconds);
}
pool.getDataSource().setLoginTimeout(seconds);
}
/** {@inheritDoc} */
@Override
public int getLoginTimeout() throws SQLException
{
HikariPool hikariPool = multiPool.values().iterator().next();
if (hikariPool != null) {
return hikariPool.getDataSource().getLoginTimeout();
if (pool != null) {
return pool.getDataSource().getLoginTimeout();
}
return 0;
@ -217,6 +192,40 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
return false;
}
/** {@inheritDoc} */
@Override
public void setMetricRegistry(Object metricRegistry)
{
boolean isAlreadySet = getMetricRegistry() != null;
super.setMetricRegistry(metricRegistry);
if (fastPathPool != null || pool != null) {
if (isAlreadySet) {
throw new IllegalStateException("MetricRegistry can only be set one time");
}
else {
pool.setMetricRegistry((MetricRegistry) super.getMetricRegistry());
}
}
}
/** {@inheritDoc} */
@Override
public void setHealthCheckRegistry(Object healthCheckRegistry)
{
boolean isAlreadySet = getHealthCheckRegistry() != null;
super.setHealthCheckRegistry(healthCheckRegistry);
if (fastPathPool != null || pool != null) {
if (isAlreadySet) {
throw new IllegalStateException("HealthCheckRegistry can only be set one time");
}
else {
pool.setHealthCheckRegistry((HealthCheckRegistry) super.getHealthCheckRegistry());
}
}
}
/**
* 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
@ -275,9 +284,7 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
shutdownHelper(fastPathPool);
}
for (HikariPool hikariPool : multiPool.values()) {
shutdownHelper(hikariPool);
}
shutdownHelper(pool);
}
/** {@inheritDoc} */
@ -300,42 +307,4 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
((DriverDataSource) hPool.getDataSource()).shutdown();
}
}
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 == null && otherKey.username != null) {
return false;
}
else if (password != null && !password.equals(otherKey.password)) {
return false;
}
else if (password == null && otherKey.password != null) {
return false;
}
return true;
}
}
}

@ -31,6 +31,8 @@ import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
/**
* Connection provider for Hibernate 4.3.
*
@ -125,12 +127,16 @@ public class HikariConnectionProvider implements ConnectionProvider, Configurabl
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> unwrapType)
{
if (isUnwrappableAs(unwrapType)) {
return (T) this;
}
else {
throw new UnknownUnwrapTypeException(unwrapType);
}
if ( ConnectionProvider.class.equals( unwrapType ) ||
HikariConnectionProvider.class.isAssignableFrom( unwrapType ) ) {
return (T) this;
}
else if ( DataSource.class.isAssignableFrom( unwrapType ) ) {
return (T) this.hds;
}
else {
throw new UnknownUnwrapTypeException( unwrapType );
}
}
// *************************************************************************

@ -24,7 +24,7 @@ import com.codahale.metrics.CachedGauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.zaxxer.hikari.pool.BaseHikariPool;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.PoolBagEntry;
public final class CodaHaleMetricsTracker extends MetricsTracker
@ -33,7 +33,7 @@ public final class CodaHaleMetricsTracker extends MetricsTracker
private final Histogram connectionUsage;
private final MetricRegistry registry;
public CodaHaleMetricsTracker(final BaseHikariPool pool, final MetricRegistry registry) {
public CodaHaleMetricsTracker(final HikariPool pool, final MetricRegistry registry) {
super(pool);
this.registry = registry;

@ -29,7 +29,7 @@ import com.codahale.metrics.Timer;
import com.codahale.metrics.health.HealthCheck;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.pool.BaseHikariPool;
import com.zaxxer.hikari.pool.HikariPool;
/**
* Provides Dropwizard HealthChecks. Two health checks are provided:
@ -54,7 +54,7 @@ public final class CodahaleHealthChecker
* @param pool the pool to register health checks for
* @param registry the HealthCheckRegistry into which checks will be registered
*/
public static void registerHealthChecks(final BaseHikariPool pool, final HealthCheckRegistry registry)
public static void registerHealthChecks(final HikariPool pool, final HealthCheckRegistry registry)
{
final HikariConfig hikariConfig = pool.getConfiguration();
final Properties healthCheckProperties = hikariConfig.getHealthCheckProperties();
@ -87,10 +87,10 @@ public final class CodahaleHealthChecker
private static class ConnectivityHealthCheck extends HealthCheck
{
private final BaseHikariPool pool;
private final HikariPool pool;
private final long checkTimeoutMs;
ConnectivityHealthCheck(final BaseHikariPool pool, final long checkTimeoutMs)
ConnectivityHealthCheck(final HikariPool pool, final long checkTimeoutMs)
{
this.pool = pool;
this.checkTimeoutMs = (checkTimeoutMs > 0 && checkTimeoutMs != Integer.MAX_VALUE ? checkTimeoutMs : TimeUnit.SECONDS.toMillis(10));

@ -16,7 +16,7 @@
package com.zaxxer.hikari.metrics;
import com.zaxxer.hikari.pool.BaseHikariPool;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.PoolBagEntry;
/**
@ -28,9 +28,9 @@ public class MetricsTracker
{
public static final MetricsContext NO_CONTEXT = new MetricsContext();
protected final BaseHikariPool pool;
protected final HikariPool pool;
public MetricsTracker(final BaseHikariPool pool)
public MetricsTracker(final HikariPool pool)
{
this.pool = pool;
}

@ -25,9 +25,9 @@ import java.util.concurrent.Semaphore;
*
* @author Brett Wooldridge
*/
public class GlobalPoolLock
class GlobalPoolLock
{
public static final GlobalPoolLock FAUX_LOCK = new GlobalPoolLock(false) {
static final GlobalPoolLock FAUX_LOCK = new GlobalPoolLock(false) {
@Override
public void acquire() {}
@ -41,15 +41,13 @@ public class GlobalPoolLock
public void resume() {}
};
public static final GlobalPoolLock SUSPEND_RESUME_LOCK = new GlobalPoolLock(true);
private static final int MAX_PERMITS = 10000;
private final Semaphore acquisitionSemaphore;
/**
* Default constructor
*/
private GlobalPoolLock(final boolean createSemaphore) {
GlobalPoolLock(final boolean createSemaphore) {
acquisitionSemaphore = (createSemaphore ? new Semaphore(MAX_PERMITS, true) : null);
}

@ -52,7 +52,7 @@ public final class HikariMBeanElf
* @param configuration a HikariConfig instance
* @param pool a HikariPool instance
*/
public static void registerMBeans(final HikariConfig configuration, final BaseHikariPool pool)
public static void registerMBeans(final HikariConfig configuration, final HikariPool pool)
{
if (!configuration.isRegisterMbeans()) {
return;
@ -82,7 +82,7 @@ public final class HikariMBeanElf
* @param configuration a HikariConfig instance
* @param pool a HikariPool instance
*/
public static void unregisterMBeans(final HikariConfig configuration, final BaseHikariPool pool)
public static void unregisterMBeans(final HikariConfig configuration, final HikariPool pool)
{
try {
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

@ -21,10 +21,10 @@ import static com.zaxxer.hikari.pool.HikariMBeanElf.unregisterMBeans;
import static com.zaxxer.hikari.util.IConcurrentBagEntry.STATE_IN_USE;
import static com.zaxxer.hikari.util.IConcurrentBagEntry.STATE_NOT_IN_USE;
import static com.zaxxer.hikari.util.IConcurrentBagEntry.STATE_REMOVED;
import static com.zaxxer.hikari.util.UtilityElf.createInstance;
import static com.zaxxer.hikari.util.UtilityElf.createThreadPoolExecutor;
import static com.zaxxer.hikari.util.UtilityElf.elapsedTimeMs;
import static com.zaxxer.hikari.util.UtilityElf.getTransactionIsolation;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import java.sql.Connection;
import java.sql.SQLException;
@ -46,7 +46,6 @@ import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.IConnectionCustomizer;
import com.zaxxer.hikari.metrics.CodaHaleMetricsTracker;
import com.zaxxer.hikari.metrics.CodahaleHealthChecker;
import com.zaxxer.hikari.metrics.MetricsTracker;
@ -57,7 +56,6 @@ import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.DefaultThreadFactory;
import com.zaxxer.hikari.util.IBagStateListener;
import com.zaxxer.hikari.util.LeakTask;
/**
* This is the primary connection pool class that provides the basic
@ -65,9 +63,9 @@ import com.zaxxer.hikari.util.LeakTask;
*
* @author Brett Wooldridge
*/
public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListener
public class HikariPool implements HikariPoolMBean, IBagStateListener
{
protected static final Logger LOGGER = LoggerFactory.getLogger("HikariPool");
protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
private static final long ALIVE_BYPASS_WINDOW = Long.getLong("com.zaxxer.hikari.aliveBypassWindow", 1000L);
protected static final int POOL_RUNNING = 0;
@ -96,79 +94,61 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
private final LeakTask leakTask;
private final DataSource dataSource;
private final MetricsTracker metricsTracker;
private final GlobalPoolLock suspendResumeLock;
private final IConnectionCustomizer connectionCustomizer;
private final AtomicReference<Throwable> lastConnectionFailure;
private final String username;
private final String password;
private final boolean isRecordMetrics;
/**
* Construct a HikariPool with the specified configuration.
*
* @param configuration a HikariConfig instance
*/
public BaseHikariPool(HikariConfig configuration)
{
this(configuration, configuration.getUsername(), configuration.getPassword());
}
private volatile MetricsTracker metricsTracker;
private volatile boolean isRecordMetrics;
/**
* Construct a HikariPool with the specified configuration. We cache lots of configuration
* items in class-local final members for speed.
* Construct a HikariPool with the specified configuration.
*
* @param configuration a HikariConfig instance
* @param username authentication username
* @param password authentication password
* @param config a HikariConfig instance
*/
public BaseHikariPool(HikariConfig configuration, String username, String password)
{
this.username = username;
this.password = password;
this.configuration = configuration;
this.poolUtils = new PoolUtilities(configuration);
this.connectionBag = createConcurrentBag(this);
public HikariPool(HikariConfig config)
{
this.username = config.getUsername();
this.password = config.getPassword();
this.configuration = config;
this.poolUtils = new PoolUtilities(config);
this.connectionBag = new ConcurrentBag<PoolBagEntry>(this);
this.totalConnections = new AtomicInteger();
this.connectionTimeout = configuration.getConnectionTimeout();
this.validationTimeout = configuration.getValidationTimeout();
this.connectionTimeout = config.getConnectionTimeout();
this.validationTimeout = config.getValidationTimeout();
this.lastConnectionFailure = new AtomicReference<Throwable>();
this.isReadOnly = configuration.isReadOnly();
this.isAutoCommit = configuration.isAutoCommit();
this.isReadOnly = config.isReadOnly();
this.isAutoCommit = config.isAutoCommit();
this.suspendResumeLock = configuration.isAllowPoolSuspension() ? GlobalPoolLock.SUSPEND_RESUME_LOCK : GlobalPoolLock.FAUX_LOCK;
this.suspendResumeLock = config.isAllowPoolSuspension() ? new GlobalPoolLock(true) : GlobalPoolLock.FAUX_LOCK;
this.catalog = configuration.getCatalog();
this.connectionCustomizer = initializeCustomizer();
this.transactionIsolation = getTransactionIsolation(configuration.getTransactionIsolation());
this.isIsolateInternalQueries = configuration.isIsolateInternalQueries();
this.isUseJdbc4Validation = configuration.getConnectionTestQuery() == null;
this.catalog = config.getCatalog();
this.transactionIsolation = getTransactionIsolation(config.getTransactionIsolation());
this.isIsolateInternalQueries = config.isIsolateInternalQueries();
this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;
this.isRecordMetrics = configuration.getMetricRegistry() != null;
this.metricsTracker = (isRecordMetrics ? new CodaHaleMetricsTracker(this, (MetricRegistry) configuration.getMetricRegistry()) : new MetricsTracker(this));
setMetricRegistry(config.getMetricRegistry());
setHealthCheckRegistry(config.getHealthCheckRegistry());
this.dataSource = poolUtils.initializeDataSource(configuration.getDataSourceClassName(), configuration.getDataSource(), configuration.getDataSourceProperties(), configuration.getJdbcUrl(), username, password);
this.dataSource = poolUtils.initializeDataSource(config.getDataSourceClassName(), config.getDataSource(), config.getDataSourceProperties(), config.getDriverClassName(), config.getJdbcUrl(), username, password);
this.addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler (pool " + configuration.getPoolName() + ")", configuration.getThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
this.closeConnectionExecutor = createThreadPoolExecutor(4, "HikariCP connection closer (pool " + configuration.getPoolName() + ")", configuration.getThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
this.addConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), "HikariCP connection filler (pool " + config.getPoolName() + ")", config.getThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
this.closeConnectionExecutor = createThreadPoolExecutor(4, "HikariCP connection closer (pool " + config.getPoolName() + ")", config.getThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
long delayPeriod = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", TimeUnit.SECONDS.toMillis(30L));
ThreadFactory threadFactory = configuration.getThreadFactory() != null ? configuration.getThreadFactory() : new DefaultThreadFactory("Hikari Housekeeping Timer (pool " + configuration.getPoolName() + ")", true);
ThreadFactory threadFactory = config.getThreadFactory() != null ? config.getThreadFactory() : new DefaultThreadFactory("Hikari Housekeeping Timer (pool " + config.getPoolName() + ")", true);
this.houseKeepingExecutorService = new ScheduledThreadPoolExecutor(1, threadFactory, new ThreadPoolExecutor.DiscardPolicy());
this.houseKeepingExecutorService.scheduleAtFixedRate(getHouseKeeper(), delayPeriod, delayPeriod, TimeUnit.MILLISECONDS);
this.houseKeepingExecutorService.scheduleAtFixedRate(new HouseKeeper(), delayPeriod, delayPeriod, TimeUnit.MILLISECONDS);
this.houseKeepingExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
this.houseKeepingExecutorService.setRemoveOnCancelPolicy(true);
this.leakTask = (configuration.getLeakDetectionThreshold() == 0) ? LeakTask.NO_LEAK : new LeakTask(configuration.getLeakDetectionThreshold(), houseKeepingExecutorService);
if (configuration.getHealthCheckRegistry() != null) {
CodahaleHealthChecker.registerHealthChecks(this, (HealthCheckRegistry) configuration.getHealthCheckRegistry());
}
this.leakTask = (config.getLeakDetectionThreshold() == 0) ? LeakTask.NO_LEAK : new LeakTask(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
poolUtils.setLoginTimeout(dataSource, connectionTimeout);
registerMBeans(configuration, this);
registerMBeans(config, this);
initializeConnections();
}
@ -206,13 +186,13 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
final long now = System.currentTimeMillis();
if (bagEntry.evicted || (now - bagEntry.lastAccess > ALIVE_BYPASS_WINDOW && !isConnectionAlive(bagEntry.connection))) {
closeConnection(bagEntry); // Throw away the dead connection and try again
closeConnection(bagEntry, "connection evicted or dead"); // Throw away the dead connection and try again
timeout = hardTimeout - elapsedTimeMs(start);
}
else {
metricsContext.setConnectionLastOpen(bagEntry, now);
metricsContext.stop();
return ProxyFactory.getProxyConnection((HikariPool) this, bagEntry, leakTask.start());
return ProxyFactory.getProxyConnection(this, bagEntry, leakTask.start(bagEntry));
}
}
while (timeout > 0L);
@ -228,7 +208,6 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
throw new SQLTimeoutException(String.format("Timeout after %dms of waiting for a connection.", elapsedTimeMs(start)), lastConnectionFailure.getAndSet(null));
}
/**
* Release a connection back to the pool, or permanently close it if it is broken.
*
@ -240,7 +219,7 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
if (bagEntry.evicted) {
LOGGER.debug("Connection returned to pool {} is broken or evicted. Closing connection.", configuration.getPoolName());
closeConnection(bagEntry);
closeConnection(bagEntry, "connection broken or evicted");
}
else {
bagEntry.lastAccess = System.currentTimeMillis();
@ -295,7 +274,7 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
*/
public final void evictConnection(IHikariConnectionProxy proxyConnection)
{
closeConnection(proxyConnection.getPoolBagEntry());
closeConnection(proxyConnection.getPoolBagEntry(), "connection evicted by user");
}
/**
@ -324,6 +303,31 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
return configuration.getPoolName();
}
// ***********************************************************************
// IBagStateListener callback
// ***********************************************************************
/** {@inheritDoc} */
@Override
public void addBagItem()
{
class AddConnection implements Runnable
{
public void run()
{
long sleepBackoff = 200L;
final int maxPoolSize = configuration.getMaximumPoolSize();
while (poolState == POOL_RUNNING && totalConnections.get() < maxPoolSize && !addConnection()) {
// If we got into the loop, addConnection() failed, so we sleep and retry
quietlySleep(sleepBackoff);
sleepBackoff = Math.min(connectionTimeout / 2, (long) ((double) sleepBackoff * 1.5));
}
}
}
addConnectionExecutor.execute(new AddConnection());
}
// ***********************************************************************
// HikariPoolMBean methods
// ***********************************************************************
@ -356,6 +360,21 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
return connectionBag.getPendingQueue();
}
/** {@inheritDoc} */
@Override
public void softEvictConnections()
{
for (PoolBagEntry bagEntry : connectionBag.values(STATE_IN_USE)) {
bagEntry.evicted = true;
}
for (PoolBagEntry bagEntry : connectionBag.values(STATE_NOT_IN_USE)) {
if (connectionBag.reserve(bagEntry)) {
closeConnection(bagEntry, "connection evicted by user");
}
}
}
/** {@inheritDoc} */
@Override
public final void suspendPool()
@ -380,6 +399,24 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
}
}
public void setMetricRegistry(Object metricRegistry)
{
this.isRecordMetrics = metricRegistry != null;
if (isRecordMetrics) {
this.metricsTracker = new CodaHaleMetricsTracker(this, (MetricRegistry) metricRegistry);
}
else {
this.metricsTracker = new MetricsTracker(this);
}
}
public void setHealthCheckRegistry(Object healthCheckRegistry)
{
if (healthCheckRegistry != null) {
CodahaleHealthChecker.registerHealthChecks(this, (HealthCheckRegistry) healthCheckRegistry);
}
}
// ***********************************************************************
// Protected methods
// ***********************************************************************
@ -404,23 +441,27 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation);
poolUtils.setupConnection(connection, isAutoCommit, isReadOnly, transactionIsolation, catalog);
connectionCustomizer.customize(connection);
poolUtils.executeSql(connection, configuration.getConnectionInitSql(), isAutoCommit);
poolUtils.setNetworkTimeout(connection, originalTimeout);
connectionBag.add(new PoolBagEntry(connection, this));
lastConnectionFailure.set(null);
LOGGER.debug("Connection {} added to pool {} ", connection, configuration.getPoolName());
return true;
}
catch (Exception e) {
totalConnections.decrementAndGet(); // We failed so undo speculative increment of totalConnections
lastConnectionFailure.set(e);
poolUtils.quietlyCloseConnection(connection);
LOGGER.debug("Connection attempt to database {} failed: {}", configuration.getPoolName(), e.getMessage(), e);
LOGGER.debug("Connection attempt to database in pool {} failed: {}", configuration.getPoolName(), e.getMessage(), e);
poolUtils.quietlyCloseConnection(connection, "exception during connection creation");
return false;
}
}
totalConnections.decrementAndGet(); // We failed or pool is max, so undo speculative increment of totalConnections
return false;
else {
totalConnections.decrementAndGet(); // Pool is maxed out, so undo speculative increment of totalConnections
lastConnectionFailure.set(new SQLException(String.format("HikariCP pool %s is at maximum capacity", configuration.getPoolName())));
return true;
}
}
/**
@ -442,72 +483,25 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
}
}
// ***********************************************************************
// Abstract methods
// ***********************************************************************
/**
* Permanently close the real (underlying) connection (eat any exception).
*
* @param connectionProxy the connection to actually close
*/
protected abstract void closeConnection(final PoolBagEntry bagEntry);
/**
* Attempt to abort() active connections on Java7+, or close() them on Java6.
* @param assassinExecutor
*
* @throws InterruptedException
*/
protected abstract void abortActiveConnections(final ExecutorService assassinExecutor) throws InterruptedException;
/**
* Create the JVM version-specific ConcurrentBag instance used by the pool.
*
* @param listener the IBagStateListener instance
* @return a ConcurrentBag instance
*/
protected abstract ConcurrentBag<PoolBagEntry> createConcurrentBag(IBagStateListener listener);
/**
* Create the JVM version-specific Housekeeping runnable instance used by the pool.
* @return the HouseKeeper instance
*/
protected abstract Runnable getHouseKeeper();
// ***********************************************************************
// Private methods
// ***********************************************************************
/**
* Fill the pool up to the minimum size.
*/
private void initializeConnections()
protected void closeConnection(final PoolBagEntry bagEntry, final String closureReason)
{
if (configuration.isInitializationFailFast()) {
try {
try {
if (!addConnection()) {
shutdown();
throw new RuntimeException("Fail-fast during pool initialization", lastConnectionFailure.getAndSet(null));
}
ConnectionProxy connection = (ConnectionProxy) getConnection();
connection.getPoolBagEntry().evicted = (configuration.getMinimumIdle() == 0);
connection.close();
}
catch (SQLException e) {
shutdown();
throw new RuntimeException("Fail-fast during pool initialization", e);
}
}
catch (InterruptedException ie) {
throw new RuntimeException("Fail-fast during pool initialization", ie);
bagEntry.cancelMaxLifeTermination();
if (connectionBag.remove(bagEntry)) {
final int tc = totalConnections.decrementAndGet();
if (tc < 0) {
LOGGER.warn("Internal accounting inconsistency, totalConnections={}", tc, new Exception());
}
closeConnectionExecutor.execute(new Runnable() {
public void run() {
poolUtils.quietlyCloseConnection(bagEntry.connection, closureReason);
}
});
}
fillPool();
}
/**
@ -517,7 +511,7 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
* @param timeoutMs the timeout before we consider the test a failure
* @return true if the connection is alive, false if it is not alive or we timed out
*/
private boolean isConnectionAlive(final Connection connection)
protected boolean isConnectionAlive(final Connection connection)
{
try {
int timeoutSec = (int) TimeUnit.MILLISECONDS.toSeconds(validationTimeout);
@ -547,28 +541,64 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
}
}
// ***********************************************************************
// Private methods
// ***********************************************************************
/**
* Construct the user's connection customizer, if specified.
* Attempt to abort() active connections, or close() them.
*
* @return an IConnectionCustomizer instance
* @throws InterruptedException
*/
private IConnectionCustomizer initializeCustomizer()
private void abortActiveConnections(final ExecutorService assassinExecutor) throws InterruptedException
{
if (configuration.getConnectionCustomizerClassName() != null) {
return createInstance(configuration.getConnectionCustomizerClassName(), IConnectionCustomizer.class);
for (PoolBagEntry bagEntry : connectionBag.values(STATE_IN_USE)) {
try {
bagEntry.aborted = bagEntry.evicted = true;
bagEntry.connection.abort(assassinExecutor);
}
catch (Throwable e) {
if (e instanceof InterruptedException) {
throw (InterruptedException) e;
}
poolUtils.quietlyCloseConnection(bagEntry.connection, "connection aborted during shutdown");
}
finally {
if (connectionBag.remove(bagEntry)) {
totalConnections.decrementAndGet();
}
}
}
return configuration.getConnectionCustomizer();
}
/**
* @param healthCheckRegistry
* Fill the pool up to the minimum size.
*/
private void registerHealthChecks(Object healthCheckRegistry)
private void initializeConnections()
{
if (healthCheckRegistry != null) {
if (configuration.isInitializationFailFast()) {
try {
try {
if (!addConnection()) {
shutdown();
throw new PoolInitializationException(lastConnectionFailure.getAndSet(null));
}
ConnectionProxy connection = (ConnectionProxy) getConnection();
connection.getPoolBagEntry().evicted = (configuration.getMinimumIdle() == 0);
connection.close();
}
catch (SQLException e) {
shutdown();
throw new PoolInitializationException(e);
}
}
catch (InterruptedException ie) {
throw new PoolInitializationException(ie);
}
}
fillPool();
}
public final void logPoolState(String... prefix)
@ -579,4 +609,43 @@ public abstract class BaseHikariPool implements HikariPoolMBean, IBagStateListen
getTotalConnections(), getActiveConnections(), getIdleConnections(), getThreadsAwaitingConnection());
}
}
// ***********************************************************************
// Non-anonymous Inner-classes
// ***********************************************************************
/**
* The house keeping task to retire idle connections.
*/
private class HouseKeeper implements Runnable
{
@Override
public void run()
{
logPoolState("Before cleanup ");
connectionTimeout = configuration.getConnectionTimeout(); // refresh member in case it changed
final long now = System.currentTimeMillis();
final long idleTimeout = configuration.getIdleTimeout();
for (PoolBagEntry bagEntry : connectionBag.values(STATE_NOT_IN_USE)) {
if (connectionBag.reserve(bagEntry)) {
if (bagEntry.evicted) {
closeConnection(bagEntry, "connection evicted");
}
else if (idleTimeout > 0L && now > bagEntry.lastAccess + idleTimeout) {
closeConnection(bagEntry, "connection passed idleTimeout");
}
else {
connectionBag.unreserve(bagEntry);
}
}
}
logPoolState("After cleanup ");
fillPool(); // Try to maintain minimum connections
}
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.zaxxer.hikari.util;
package com.zaxxer.hikari.pool;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@ -37,6 +37,7 @@ public class LeakTask implements Runnable
private ScheduledExecutorService executorService;
private long leakDetectionThreshold;
private ScheduledFuture<?> scheduledFuture;
private PoolBagEntry bagEntry;
private Exception exception;
static
@ -46,7 +47,7 @@ public class LeakTask implements Runnable
public void cancel() {};
@Override
public LeakTask start()
public LeakTask start(final PoolBagEntry bagEntry)
{
return this;
}
@ -63,15 +64,16 @@ public class LeakTask implements Runnable
{
}
private LeakTask(final LeakTask parent)
private LeakTask(final LeakTask parent, final PoolBagEntry bagEntry)
{
exception = new Exception("Apparent connection leak detected");
this.exception = new Exception("Apparent connection leak detected");
this.bagEntry = bagEntry;
scheduledFuture = parent.executorService.schedule(this, parent.leakDetectionThreshold, TimeUnit.MILLISECONDS);
}
public LeakTask start()
public LeakTask start(final PoolBagEntry bagEntry)
{
return new LeakTask(this);
return new LeakTask(this, bagEntry);
}
/** {@inheritDoc} */
@ -79,11 +81,11 @@ public class LeakTask implements Runnable
public void run()
{
final StackTraceElement[] stackTrace = exception.getStackTrace();
final StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 3];
System.arraycopy(stackTrace, 3, trace, 0, trace.length);
final StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 5];
System.arraycopy(stackTrace, 5, trace, 0, trace.length);
exception.setStackTrace(trace);
LOGGER.warn("Connection leak detection triggered, stack trace follows", exception);
LOGGER.warn("Connection leak detection triggered for connection {}, stack trace follows", bagEntry.connection.toString(), exception);
}
public void cancel()

@ -40,7 +40,7 @@ public final class PoolBagEntry implements IConcurrentBagEntry
private volatile ScheduledFuture<?> endOfLife;
public PoolBagEntry(final Connection connection, final BaseHikariPool pool) {
public PoolBagEntry(final Connection connection, final HikariPool pool) {
this.connection = connection;
this.lastAccess = System.currentTimeMillis();
@ -51,7 +51,7 @@ public final class PoolBagEntry implements IConcurrentBagEntry
{
// If we can reserve it, close it
if (pool.connectionBag.reserve(PoolBagEntry.this)) {
pool.closeConnection(PoolBagEntry.this);
pool.closeConnection(PoolBagEntry.this, "connection reached maxLifetime");
}
else {
// else the connection is "in-use" and we mark it for eviction by pool.releaseConnection() or the housekeeper

@ -0,0 +1,35 @@
/*
* Copyright (C) 2015 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.pool;
/**
* A custom exception thrown if pool initialization fails.
*
* @author Brett Wooldridge
*/
public class PoolInitializationException extends RuntimeException
{
private static final long serialVersionUID = 929872118275916520L;
/**
* Construct an exception, possibly wrapping the provided Throwable as the cause.
*/
public PoolInitializationException(Throwable t)
{
super("Exception during pool initialization", t);
}
}

@ -45,18 +45,20 @@ public final class PoolUtilities
* Close connection and eat any exception.
*
* @param connection the connection to close
* @param closureReason the reason the connection was closed (if known)
*/
public void quietlyCloseConnection(final Connection connection)
public void quietlyCloseConnection(final Connection connection, final String closureReason)
{
final String addendum = closureReason != null ? " (" + closureReason + ")" : "";
try {
LOGGER.debug("Closing connection {}", connection);
LOGGER.debug("Closing connection {} in pool {}{}", connection, poolName, addendum);
if (connection != null && !connection.isClosed()) {
setNetworkTimeout(connection, TimeUnit.SECONDS.toMillis(30));
connection.close();
}
}
catch (Throwable e) {
LOGGER.debug("{} - Exception closing connection {}", poolName, connection.toString(), e);
LOGGER.debug("Exception closing connection {} in pool {}{}", connection.toString(), poolName, addendum, e);
}
}
@ -90,12 +92,13 @@ public final class PoolUtilities
* @param dsClassName a DataSource class name (optional)
* @param dataSource a DataSource instance (optional)
* @param dataSourceProperties a Properties instance of DataSource properties
* @param driverClassName the JDBC driver class name (optional)
* @param jdbcUrl a JDBC connection URL (optional)
* @param username a username (optional)
* @param password a password (optional)
* @return a DataSource instance
*/
public DataSource initializeDataSource(final String dsClassName, DataSource dataSource, final Properties dataSourceProperties, final String jdbcUrl, final String username, final String password)
public DataSource initializeDataSource(final String dsClassName, DataSource dataSource, final Properties dataSourceProperties, final String driverClassName, final String jdbcUrl, final String username, final String password)
{
try {
if (dataSource == null && dsClassName != null) {
@ -104,7 +107,7 @@ public final class PoolUtilities
return dataSource;
}
else if (jdbcUrl != null) {
return new DriverDataSource(jdbcUrl, dataSourceProperties, username, password);
return new DriverDataSource(jdbcUrl, driverClassName, dataSourceProperties, username, password);
}
return dataSource;

@ -30,9 +30,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.LeakTask;
import com.zaxxer.hikari.pool.PoolBagEntry;
import com.zaxxer.hikari.util.FastList;
import com.zaxxer.hikari.util.LeakTask;
/**
* This is the proxy class for java.sql.Connection.
@ -106,10 +106,10 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
boolean isForceClose = sqlState.startsWith("08") | SQL_ERRORS.contains(sqlState);
if (isForceClose) {
bagEntry.evicted = true;
LOGGER.warn(String.format("Connection %s (%s) marked as broken because of SQLSTATE(%s), ErrorCode(%d).", delegate.toString(),
parentPool.toString(), sqlState, sqle.getErrorCode()), sqle);
LOGGER.warn("Connection {} ({}) marked as broken because of SQLSTATE({}), ErrorCode({}).", delegate.toString(),
parentPool.toString(), sqlState, sqle.getErrorCode(), sqle);
}
else if (sqle.getNextException() instanceof SQLException && sqle != sqle.getNextException()) {
else if (sqle.getNextException() != null && sqle != sqle.getNextException()) {
checkException(sqle.getNextException());
}
}

@ -0,0 +1,265 @@
/*
* 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.proxy;
import java.lang.reflect.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.Modifier;
import javassist.NotFoundException;
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
*/
public final class JavassistProxyFactory
{
private ClassPool classPool;
static {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(JavassistProxyFactory.class.getClassLoader());
JavassistProxyFactory proxyFactoryFactory = new JavassistProxyFactory();
proxyFactoryFactory.modifyProxyFactory();
}
catch (Exception e) {
throw new RuntimeException("Fatal exception during proxy generation", 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()
{
// no-op
}
private JavassistProxyFactory() throws Exception
{
classPool = new ClassPool();
classPool.importPackage("java.sql");
classPool.appendClassPath(new LoaderClassPath(this.getClass().getClassLoader()));
// Cast is not needed for these
String methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { throw checkException(e); } }";
generateProxyClass(Connection.class, ConnectionProxy.class, methodBody);
generateProxyClass(Statement.class, StatementProxy.class, methodBody);
generateProxyClass(ResultSet.class, ResultSetProxy.class, methodBody);
// For these we have to cast the delegate
methodBody = "{ try { return ((cast) delegate).method($$); } catch (SQLException e) { throw checkException(e); } }";
generateProxyClass(PreparedStatement.class, PreparedStatementProxy.class, methodBody);
generateProxyClass(CallableStatement.class, CallableStatementProxy.class, methodBody);
}
private void modifyProxyFactory() throws Exception
{
String packageName = JavassistProxyFactory.class.getPackage().getName();
CtClass proxyCt = classPool.getCtClass("com.zaxxer.hikari.proxy.ProxyFactory");
for (CtMethod method : proxyCt.getMethods()) {
switch (method.getName()) {
case "getProxyConnection":
method.setBody("{return new " + packageName + ".ConnectionJavassistProxy($$);}");
break;
case "getProxyStatement":
method.setBody("{return new " + packageName + ".StatementJavassistProxy($$);}");
break;
case "getProxyPreparedStatement":
method.setBody("{return new " + packageName + ".PreparedStatementJavassistProxy($$);}");
break;
case "getProxyCallableStatement":
method.setBody("{return new " + packageName + ".CallableStatementJavassistProxy($$);}");
break;
case "getProxyResultSet":
method.setBody("{return new " + packageName + ".ResultSetJavassistProxy($$);}");
break;
}
}
proxyCt.toClass(classPool.getClassLoader(), getClass().getProtectionDomain());
}
/**
* Generate Javassist Proxy Classes
*/
@SuppressWarnings("unchecked")
private <T> Class<T> generateProxyClass(Class<T> primaryInterface, Class<?> superClass, String methodBody) throws Exception
{
// Make a new class that extends one of the JavaProxy classes (ie. superClass); use the name to XxxJavassistProxy instead of XxxProxy
String superClassName = superClass.getName();
CtClass superClassCt = classPool.getCtClass(superClassName);
CtClass targetCt = classPool.makeClass(superClassName.replace("Proxy", "JavassistProxy"), superClassCt);
targetCt.setModifiers(Modifier.FINAL);
// Make a set of method signatures we inherit implementation for, so we don't generate delegates for these
Set<String> superSigs = new HashSet<String>();
for (CtMethod method : superClassCt.getMethods()) {
if ((method.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
superSigs.add(method.getName() + method.getSignature());
}
}
Set<String> methods = new HashSet<String>();
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(primaryInterface);
for (Class<?> intf : interfaces) {
CtClass intfCt = classPool.getCtClass(intf.getName());
targetCt.addInterface(intfCt);
for (CtMethod intfMethod : intfCt.getDeclaredMethods()) {
final String signature = intfMethod.getName() + intfMethod.getSignature();
// don't generate delegates for methods we override
if (superSigs.contains(signature)) {
continue;
}
// Ignore already added methods that come from other interfaces
if (methods.contains(signature)) {
continue;
}
// Ignore default methods (only for Jre8 or later)
if (isDefaultMethod(intf, intfCt, intfMethod)) {
continue;
}
// Track what methods we've added
methods.add(signature);
// Clone the method we want to inject into
CtMethod method = CtNewMethod.copy(intfMethod, targetCt, null);
String modifiedBody = methodBody;
// If the super-Proxy has concrete methods (non-abstract), transform the call into a simple super.method() call
CtMethod superMethod = superClassCt.getMethod(intfMethod.getName(), intfMethod.getSignature());
if ((superMethod.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT) {
modifiedBody = modifiedBody.replace("((cast) ", "");
modifiedBody = modifiedBody.replace("delegate", "super");
modifiedBody = modifiedBody.replace("super)", "super");
}
modifiedBody = modifiedBody.replace("cast", primaryInterface.getName());
// Generate a method that simply invokes the same method on the delegate
if (isThrowsSqlException(intfMethod)) {
modifiedBody = modifiedBody.replace("method", method.getName());
}
else {
modifiedBody = "{ return ((cast) delegate).method($$); }".replace("method", method.getName()).replace("cast", primaryInterface.getName());
}
if (method.getReturnType() == CtClass.voidType) {
modifiedBody = modifiedBody.replace("return", "");
}
method.setBody(modifiedBody);
targetCt.addMethod(method);
}
}
return targetCt.toClass(classPool.getClassLoader(), getClass().getProtectionDomain());
}
private boolean isThrowsSqlException(CtMethod method)
{
try {
for (CtClass clazz : method.getExceptionTypes()) {
if (clazz.getSimpleName().equals("SQLException")) {
return true;
}
}
}
catch (NotFoundException e) {
// fall thru
}
return false;
}
private boolean isDefaultMethod(Class<?> intf, CtClass intfCt, CtMethod intfMethod) throws Exception
{
List<Class<?>> paramTypes = new ArrayList<Class<?>>();
for (CtClass pt : intfMethod.getParameterTypes()) {
paramTypes.add(toJavaClass(pt));
}
return intf.getDeclaredMethod(intfMethod.getName(), paramTypes.toArray(new Class[0])).toString().contains("default ");
}
private Class<?> toJavaClass(CtClass cls) throws Exception
{
if (cls.getName().endsWith("[]")) {
return Array.newInstance(toJavaClass(cls.getName().replace("[]", "")), 0).getClass();
}
else {
return toJavaClass(cls.getName());
}
}
private Class<?> toJavaClass(String cn) throws Exception
{
switch (cn) {
case "int":
return int.class;
case "long":
return long.class;
case "short":
return short.class;
case "byte":
return byte.class;
case "float":
return float.class;
case "double":
return double.class;
case "boolean":
return boolean.class;
case "char":
return char.class;
case "void":
return void.class;
default:
return Class.forName(cn);
}
}
}

@ -23,8 +23,8 @@ import java.sql.ResultSet;
import java.sql.Statement;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.LeakTask;
import com.zaxxer.hikari.pool.PoolBagEntry;
import com.zaxxer.hikari.util.LeakTask;
/**
* A factory class that produces proxies around instances of the standard

@ -303,7 +303,6 @@ public class ConcurrentBag<T extends IConcurrentBagEntry>
}
}
/**
* Our private synchronizer that handles notify/wait type semantics.
*/

@ -21,6 +21,8 @@ import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Enumeration;
import java.util.Map.Entry;
import java.util.Properties;
import javax.sql.DataSource;
@ -31,12 +33,16 @@ public final class DriverDataSource implements DataSource
private final Properties driverProperties;
private final Driver driver;
public DriverDataSource(String jdbcUrl, Properties properties, String username, String password)
public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password)
{
try {
this.jdbcUrl = jdbcUrl;
this.driverProperties = new Properties();
this.driverProperties.putAll(properties);
for (Entry<Object, Object> entry : properties.entrySet()) {
driverProperties.setProperty(entry.getKey().toString(), entry.getValue().toString());
}
if (username != null) {
driverProperties.put("user", driverProperties.getProperty("user", username));
}
@ -44,7 +50,27 @@ public final class DriverDataSource implements DataSource
driverProperties.put("password", driverProperties.getProperty("password", password));
}
driver = DriverManager.getDriver(jdbcUrl);
if (driverClassName != null) {
Driver matched = null;
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver d = drivers.nextElement();
if (d.getClass().getName().equals(driverClassName)) {
matched = d;
break;
}
}
if (matched != null) {
driver = matched;
}
else {
throw new IllegalArgumentException("Driver with class name " + driverClassName + " was not found among registered drivers");
}
}
else {
driver = DriverManager.getDriver(jdbcUrl);
}
}
catch (SQLException e) {
throw new RuntimeException("Unable to get driver for JDBC URL " + jdbcUrl, e);

@ -12,7 +12,6 @@ import com.zaxxer.hikari.util.UtilityElf;
public class ConnectionStateTest
{
@SuppressWarnings("deprecation")
@Test
public void testAutoCommit() throws SQLException
{
@ -20,7 +19,6 @@ public class ConnectionStateTest
ds.setAutoCommit(true);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setJdbc4ConnectionTest(false);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds.addDataSourceProperty("user", "bar");

@ -36,7 +36,6 @@ public class JdbcDriverTest
}
}
@SuppressWarnings("deprecation")
@Test
public void driverTest1() throws SQLException
{
@ -58,9 +57,6 @@ public class JdbcDriverTest
Connection connection = ds.getConnection();
connection.close();
connection = ds.getConnection("foo", "bar");
connection.close();
}
@Test

@ -29,7 +29,7 @@ import org.junit.Test;
import org.slf4j.spi.LocationAwareLogger;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.util.LeakTask;
import com.zaxxer.hikari.pool.LeakTask;
import com.zaxxer.hikari.util.UtilityElf;
/**
@ -114,7 +114,7 @@ public class MiscTest
ps.close();
String s = new String(baos.toByteArray());
Assert.assertNotNull("Exception string was null", s);
Assert.assertTrue("Expected exception to contain 'Connection leak detection' but contains *" + s + "*", s.contains("Connection leak detection"));
Assert.assertTrue("Expected exception to contain 'Apparent connection leak detected' but contains *" + s + "*", s.contains("Apparent connection leak detected"));
}
finally
{

@ -205,7 +205,6 @@ public class ShutdownTest
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
}
@SuppressWarnings("deprecation")
@Test
public void testAfterShutdown() throws Exception
{
@ -225,17 +224,6 @@ public class ShutdownTest
catch (SQLException e) {
Assert.assertTrue(e.getMessage().contains("Pool has been shutdown"));
}
try
{
ds.getConnection("foo", "bar");
}
catch (SQLException e) {
Assert.assertTrue(e.getMessage().contains("Pool has been shutdown"));
}
finally {
ds.shutdown();
}
}
@Test
@ -282,7 +270,7 @@ public class ShutdownTest
Assert.fail(e.getMessage());
}
finally {
new PoolUtilities(config).quietlyCloseConnection(connection);
new PoolUtilities(config).quietlyCloseConnection(connection, "because this is a test");
ds.shutdown();
}
};

@ -28,7 +28,6 @@ import org.junit.Test;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.PoolBagEntry;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.Java8ConcurrentBag;
/**
*
@ -60,7 +59,7 @@ public class TestConcurrentBag
@Test
public void testConcurrentBag() throws InterruptedException
{
ConcurrentBag<PoolBagEntry> bag = new Java8ConcurrentBag(null);
ConcurrentBag<PoolBagEntry> bag = new ConcurrentBag<PoolBagEntry>(null);
Assert.assertEquals(0, bag.values(8).size());
HikariPool pool = TestElf.getPool(ds);
@ -79,7 +78,7 @@ public class TestConcurrentBag
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos, true);
TestElf.setSlf4jTargetStream(Java8ConcurrentBag.class, ps);
TestElf.setSlf4jTargetStream(ConcurrentBag.class, ps);
bag.requite(reserved);
Assert.assertTrue(new String(baos.toByteArray()).contains("does not exist"));

@ -20,7 +20,6 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
@ -41,7 +40,6 @@ import com.zaxxer.hikari.util.UtilityElf;
*/
public class TestConnections
{
@SuppressWarnings("deprecation")
@Test
public void testCreate() throws SQLException
{
@ -337,39 +335,6 @@ public class TestConnections
}
}
@SuppressWarnings("deprecation")
@Test
public void testGetWithUsername() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(4);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
final HikariDataSource ds = new HikariDataSource(config);
try {
Connection connection1 = ds.getConnection("foo", "bar");
connection1.close();
Connection connection2 = ds.getConnection("foo", "bar");
connection2.close();
Assert.assertSame(connection1.unwrap(Connection.class), connection2.unwrap(Connection.class));
Connection connection3 = ds.getConnection("faz", "baf");
connection3.close();
HashMap<Object, HikariPool> multiPool = TestElf.getMultiPool(ds);
Assert.assertTrue(multiPool.size() > 1);
Object[] array = multiPool.keySet().toArray();
Assert.assertNotEquals(array[0], array[1]);
}
finally {
ds.close();
}
}
@Test
public void testOldDriver() throws Exception
{

@ -0,0 +1,151 @@
/*
* 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;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.health.HealthCheck.Result;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.util.UtilityElf;
/**
* Test HikariCP/CodaHale metrics integration.
*
* @author Brett Wooldridge
*/
public class TestMetrics
{
@Test
public void testMetricWait() throws SQLException
{
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailFast(false);
config.setPoolName("test");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
ds.getConnection().close();
Timer timer = metricRegistry.getTimers(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(String name, Metric metric)
{
return "test.pool.Wait".equals(MetricRegistry.name("test", "pool", "Wait"));
}
}).values().iterator().next();
Assert.assertEquals(1, timer.getCount());
Assert.assertTrue(timer.getMeanRate() > 0.0);
}
finally {
ds.close();
}
}
@Test
public void testMetricUsage() throws SQLException
{
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailFast(false);
config.setPoolName("test");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
Connection connection = ds.getConnection();
UtilityElf.quietlySleep(250L);
connection.close();
Histogram histo = metricRegistry.getHistograms(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(String name, Metric metric)
{
return "test.pool.Usage".equals(MetricRegistry.name("test", "pool", "Usage"));
}
}).values().iterator().next();
Assert.assertEquals(1, histo.getCount());
double seventyFifth = histo.getSnapshot().get75thPercentile();
Assert.assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
}
finally {
ds.close();
}
}
@Test
public void testHealthChecks() throws Exception
{
MetricRegistry metricRegistry = new MetricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(10);
config.setMetricRegistry(metricRegistry);
config.setHealthCheckRegistry(healthRegistry);
config.setPoolName("test");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.addHealthCheckProperty("connectivityCheckTimeoutMs", "1000");
config.addHealthCheckProperty("expected99thPercentileMs", "10");
HikariDataSource ds = new HikariDataSource(config);
try {
UtilityElf.quietlySleep(TimeUnit.SECONDS.toMillis(2));
Connection connection = ds.getConnection();
connection.close();
connection = ds.getConnection();
connection.close();
SortedMap<String, Result> healthChecks = healthRegistry.runHealthChecks();
Result connectivityResult = healthChecks.get("test.pool.ConnectivityCheck");
Assert.assertTrue(connectivityResult.isHealthy());
Result slaResult = healthChecks.get("test.pool.Connection99Percent");
Assert.assertTrue(slaResult.isHealthy());
}
finally {
ds.close();
}
}
}

@ -17,8 +17,6 @@ package com.zaxxer.hikari;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
@ -29,7 +27,6 @@ import com.zaxxer.hikari.mocks.StubDataSource;
/**
* @author Brett Wooldridge
*/
@SuppressWarnings("deprecation")
public class TestValidation
{
@Test
@ -41,47 +38,6 @@ public class TestValidation
Assert.assertEquals(5, config.getMinimumIdle());
}
@Test
public void validateInvalidCustomizer()
{
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setConnectionCustomizerClassName("invalid");
config.validate();
Assert.assertNull(config.getConnectionCustomizerClassName());
}
@Test
public void validateValidCustomizer()
{
try {
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setConnectionCustomizerClassName("com.zaxxer.hikari.TestValidation$TestCustomizer");
config.validate();
Assert.assertNotNull(config.getConnectionCustomizer());
}
catch (Exception e) {
Assert.fail();
}
}
@Test
public void validateValidCustomizer2()
{
try {
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setConnectionCustomizer(new TestCustomizer());
config.validate();
Assert.assertNotNull(config.getConnectionCustomizer());
}
catch (Exception e) {
Assert.fail();
}
}
@Test
public void validateMissingProperties()
{
@ -297,12 +253,4 @@ public class TestValidation
// pass
}
}
public static class TestCustomizer implements IConnectionCustomizer
{
@Override
public void customize(Connection connection) throws SQLException
{
}
}
}
Loading…
Cancel
Save