Prepare for Java 6/7 to Java 8 split.

pull/103/head
Brett Wooldridge 11 years ago
parent 74055a1d15
commit 3451c37445

@ -0,0 +1,312 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<!-- For release: mvn release:perform -Darguments=-Dgpg.passphrase=PASSPHRASE -->
<artifactId>HikariCP-java6</artifactId>
<packaging>bundle</packaging>
<name>HikariCP-java6</name>
<description>Ultimate JDBC Connection Pool</description>
<url>https://github.com/brettwooldridge/HikariCP</url>
<organization>
<name>Zaxxer.com</name>
<url>https://github.com/brettwooldridge</url>
</organization>
<scm>
<connection>scm:git:git@github.com:brettwooldridge/HikariCP.git</connection>
<developerConnection>scm:git:git@github.com:brettwooldridge/HikariCP.git</developerConnection>
<url>git@github.com:brettwooldridge/HikariCP.git</url>
</scm>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Brett Wooldridge</name>
<email>brett.wooldridge@gmail.com</email>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<felix.bundle.plugin.version>2.4.0</felix.bundle.plugin.version>
<felix.version>4.2.1</felix.version>
<pax.exam.version>3.4.0</pax.exam.version>
<pax.url.version>1.6.0</pax.url.version>
</properties>
<parent>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.18.1-GA</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.0.Final</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- OSGi test -->
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>${felix.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-link-mvn</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.url</groupId>
<artifactId>pax-url-aether</artifactId>
<version>${pax.url.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.url</groupId>
<artifactId>pax-url-reference</artifactId>
<version>${pax.url.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<extensions>true</extensions>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</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-java6</Bundle-Name>
<Export-Package>com.zaxxer.hikari,com.zaxxer.hikari.hibernate</Export-Package>
<Private-Package>com.zaxxer.hikari.*</Private-Package>
<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,
org.slf4j,
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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<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 -->
<attach>true</attach>
</configuration>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<show>public</show>
<!-- excludePackageNames>com.zaxxer.hikari.*</excludePackageNames -->
<attach>true</attach>
<maxmemory>1024m</maxmemory>
</configuration>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e
settings only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin
</artifactId>
<versionRange>[2.4.0,)</versionRange>
<goals>
<goal>manifest</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</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,91 @@
/*
* 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.osgi;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import javax.inject.Inject;
import java.io.File;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.ops4j.pax.exam.CoreOptions.*;
/**
* @author lburgazzoli
*/
@RunWith(PaxExam.class)
public class OSGiBundleTest
{
@Inject
BundleContext context;
@Configuration
public Option[] config()
{
return options(
systemProperty("org.osgi.framework.storage.clean").value("true"),
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"),
mavenBundle("org.slf4j","slf4j-api","1.7.5"),
mavenBundle("org.slf4j","slf4j-simple","1.7.5").noStart(),
mavenBundle("org.javassist", "javassist", "3.18.1-GA"),
new File("target/classes").exists()
? bundle("reference:file:target/classes")
: bundle("reference:file:../target/classes"),
junitBundles(),
cleanCaches()
);
}
@Test
public void checkInject()
{
assertNotNull(context);
}
@Test
public void checkBundle()
{
Boolean bundleFound = false;
Boolean bundleActive = false;
Bundle[] bundles = context.getBundles();
for(Bundle bundle : bundles)
{
if(bundle != null)
{
if(bundle.getSymbolicName().equals("com.zaxxer.HikariCP-java6"))
{
bundleFound = true;
if(bundle.getState() == Bundle.ACTIVE)
{
bundleActive = true;
}
}
}
}
assertTrue(bundleFound);
assertTrue(bundleActive);
}
}

@ -0,0 +1,304 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<!-- For release: mvn release:perform -Darguments=-Dgpg.passphrase=PASSPHRASE -->
<artifactId>HikariCP</artifactId>
<packaging>bundle</packaging>
<name>HikariCP</name>
<description>Ultimate JDBC Connection Pool</description>
<url>https://github.com/brettwooldridge/HikariCP</url>
<organization>
<name>Zaxxer.com</name>
<url>https://github.com/brettwooldridge</url>
</organization>
<scm>
<connection>scm:git:git@github.com:brettwooldridge/HikariCP.git</connection>
<developerConnection>scm:git:git@github.com:brettwooldridge/HikariCP.git</developerConnection>
<url>git@github.com:brettwooldridge/HikariCP.git</url>
</scm>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Brett Wooldridge</name>
<email>brett.wooldridge@gmail.com</email>
</developer>
</developers>
<parent>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.18.1-GA</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.0.Final</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- OSGi test -->
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>${felix.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-native</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-link-mvn</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.url</groupId>
<artifactId>pax-url-aether</artifactId>
<version>${pax.url.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.url</groupId>
<artifactId>pax-url-reference</artifactId>
<version>${pax.url.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<extensions>true</extensions>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</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>
<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,
org.slf4j,
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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<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 -->
<attach>true</attach>
</configuration>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<show>public</show>
<!-- excludePackageNames>com.zaxxer.hikari.*</excludePackageNames -->
<attach>true</attach>
<maxmemory>1024m</maxmemory>
</configuration>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e
settings only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin
</artifactId>
<versionRange>[2.4.0,)</versionRange>
<goals>
<goal>manifest</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</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,757 @@
/*
* 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.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.proxy.JavassistProxyFactory;
import com.zaxxer.hikari.util.PropertyBeanSetter;
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 IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10);
private static final long MAX_LIFETIME = TimeUnit.MINUTES.toMillis(30);
private static int poolNumber;
// Properties changeable at runtime through the MBean
//
private volatile long connectionTimeout;
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 metricsTrackerClassName;
private String password;
private String poolName;
private String transactionIsolationName;
private String username;
private boolean isAutoCommit;
private boolean isReadOnly;
private boolean isInitializationFailFast;
private boolean isJdbc4connectionTest;
private boolean isIsolateInternalQueries;
private boolean isRecordMetrics;
private boolean isRegisterMbeans;
private DataSource dataSource;
private Properties dataSourceProperties;
private IConnectionCustomizer customizer;
private int transactionIsolation;
static {
JavassistProxyFactory.initialize();
}
/**
* Default constructor
*/
public HikariConfig()
{
dataSourceProperties = new Properties();
connectionTimeout = CONNECTION_TIMEOUT;
idleTimeout = IDLE_TIMEOUT;
isAutoCommit = true;
isJdbc4connectionTest = true;
minIdle = -1;
maxPoolSize = 10;
maxLifetime = MAX_LIFETIME;
isRecordMetrics = false;
transactionIsolation = -1;
metricsTrackerClassName = "com.zaxxer.hikari.metrics.CodaHaleMetricsTracker";
customizer = new IConnectionCustomizer() {
@Override
public void customize(Connection connection) throws SQLException
{
}
};
}
/**
* 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.
*
* @param propertyFileName the name of the property file
*/
public HikariConfig(String propertyFileName)
{
this();
File propFile = new File(propertyFileName);
if (!propFile.isFile()) {
throw new IllegalArgumentException("Property file " + propertyFileName + " was not found.");
}
try {
FileInputStream fis = new FileInputStream(propFile);
Properties props = new Properties();
props.load(fis);
PropertyBeanSetter.setTargetFromProperties(this, props);
fis.close();
}
catch (IOException io) {
throw new RuntimeException("Error loading properties file", io);
}
}
/**
* Get the default catalog name to be set on connections.
*
* @return the default catalog name
*/
public String getCatalog()
{
return catalog;
}
/**
* Set the default catalog name to be set on connections.
*
* @param catalog the catalog name, or null
*/
public void setCatalog(String catalog)
{
this.catalog = catalog;
}
/**
* Get the name of the connection customizer class to instantiate and execute
* on all new connections.
*
* @return the name of the customizer class, or null
*/
public String getConnectionCustomizerClassName()
{
return connectionCustomizerClassName;
}
/**
* Set the name of the connection customizer class to instantiate and execute
* on all new connections.
*
* @param connectionCustomizerClassName the name of the customizer class
*/
public void setConnectionCustomizerClassName(String connectionCustomizerClassName)
{
this.connectionCustomizerClassName = connectionCustomizerClassName;
}
/**
* Get the customizer instance specified by the user.
*
* @return an instance of IConnectionCustomizer
*/
public IConnectionCustomizer getConnectionCustomizer()
{
return customizer;
}
/**
* Set the connection customizer to be used by the pool.
*
* @param customizer an instance of IConnectionCustomizer
*/
public void setConnectionCustomizer(IConnectionCustomizer customizer)
{
this.customizer = customizer;
}
/**
* 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 < 100) {
throw new IllegalArgumentException("connectionTimeout cannot be less than 100ms");
}
else {
this.connectionTimeout = connectionTimeoutMs;
}
}
/**
* 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 void setDriverClassName(String driverClassName)
{
try {
Class<?> driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
driverClass.newInstance();
this.driverClassName = driverClassName;
}
catch (Exception e) {
throw new RuntimeException("driverClassName specified class '" + driverClassName + "' could not be loaded", e);
}
}
/** {@inheritDoc} */
@Override
public long getIdleTimeout()
{
return idleTimeout;
}
/** {@inheritDoc} */
@Override
public void setIdleTimeout(long idleTimeoutMs)
{
this.idleTimeout = idleTimeoutMs;
}
public String getJdbcUrl()
{
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl)
{
this.jdbcUrl = jdbcUrl;
}
/**
* Get the default auto-commit behavior of connections in the pool.
*
* @return the default auto-commit behavior of connections
*/
public boolean isAutoCommit()
{
return isAutoCommit;
}
/**
* Set the default auto-commit behavior of connections in the pool.
*
* @param isAutoCommit the desired auto-commit default for connections
*/
public void setAutoCommit(boolean isAutoCommit)
{
this.isAutoCommit = isAutoCommit;
}
/**
* Get whether or not the construction of the pool should throw an exception
* if the minimum number of connections cannot be created.
*
* @return whether or not initialization should fail on error immediately
*/
public boolean isInitializationFailFast()
{
return isInitializationFailFast;
}
/**
* Set whether or not the construction of the pool should throw an exception
* if the minimum number of connections cannot be created.
*
* @param failFast true if the pool should fail if the minimum connections cannot be created
*/
public void setInitializationFailFast(boolean failFast)
{
isInitializationFailFast = failFast;
}
public boolean isIsolateInternalQueries()
{
return isIsolateInternalQueries;
}
public void setIsolateInternalQueries(boolean isolate)
{
this.isIsolateInternalQueries = isolate;
}
public boolean isJdbc4ConnectionTest()
{
return isJdbc4connectionTest;
}
public void setJdbc4ConnectionTest(boolean useIsValid)
{
this.isJdbc4connectionTest = useIsValid;
}
public boolean isReadOnly()
{
return isReadOnly;
}
public void setReadOnly(boolean readOnly)
{
this.isReadOnly = readOnly;
}
public boolean isRecordMetrics()
{
return isRecordMetrics;
}
/**
* Currently not supported.
* @param recordMetrics <code>true</code> if metrics should be recorded
*/
@Deprecated
public void setRecordMetrics(boolean recordMetrics)
{
this.isRecordMetrics = recordMetrics;
}
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 < 0) {
throw new IllegalArgumentException("maxPoolSize cannot be negative");
}
this.maxPoolSize = maxPoolSize;
}
/**
* Get the name of the class that implements the IMetricsTracker interface to
* be used for metrics tracking.
*
* @return the name of the class that implements the IMetricsTracker interface
*/
public String getMetricsTrackerClassName()
{
return metricsTrackerClassName;
}
/**
* Set the name of the class that implements the IMetricsTracker interface to
* be used for metrics tracking.
*
* @param className the name of the class that implements the IMetricsTracker interface
*/
public void setMetricsTrackerClassName(String className)
{
this.metricsTrackerClassName = className;
}
/** {@inheritDoc} */
@Override
public int getMinimumIdle()
{
return minIdle;
}
/** {@inheritDoc} */
@Override
public void setMinimumIdle(int minIdle)
{
if (minIdle < 0 || minIdle > maxPoolSize) {
throw new IllegalArgumentException("maxPoolSize cannot be negative or greater than maximumPoolSize");
}
this.minIdle = minIdle;
}
/**
* Get the default password to use for DataSource.getConnection(username, password) calls.
* @return the password
*/
public String getPassword()
{
return password;
}
/**
* Set the default password to use for DataSource.getConnection(username, password) calls.
* @param password the password
*/
public void setPassword(String password)
{
this.password = password;
}
/** {@inheritDoc} */
@Override
public String getPoolName()
{
return poolName;
}
/**
* 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 int getTransactionIsolation()
{
return transactionIsolation;
}
/**
* 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;
}
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 (jdbcUrl != null && driverClassName == null) {
logger.error("when specifying jdbcUrl, driverClassName must also be specified");
throw new IllegalStateException("when specifying jdbcUrl, driverClassName must also be specified");
}
else if (driverClassName != null && 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 (connectionTestQuery != null) {
isJdbc4connectionTest = false;
}
else if (!isJdbc4connectionTest) {
logger.error("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified");
throw new IllegalStateException("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified");
}
if (transactionIsolationName != null) {
try {
Field field = Connection.class.getField(transactionIsolationName);
int level = field.getInt(null);
this.transactionIsolation = level;
}
catch (Exception e) {
throw new IllegalArgumentException("Invalid transaction isolation value: " + transactionIsolationName);
}
}
poolName = "HikariPool-" + poolNumber++;
logConfiguration();
}
private void validateNumerics()
{
Logger logger = LoggerFactory.getLogger(getClass());
if (connectionTimeout == Integer.MAX_VALUE) {
logger.warn("No connection wait timeout is set, this might cause an infinite wait");
}
else if (connectionTimeout < TimeUnit.MILLISECONDS.toMillis(250)) {
logger.warn("connectionTimeout is less than 250ms, did you specify the wrong time unit? Using default instead");
connectionTimeout = CONNECTION_TIMEOUT;
}
if (minIdle < 0) {
minIdle = maxPoolSize;
}
if (idleTimeout < 0) {
logger.error("idleTimeout cannot be negative.");
throw new IllegalArgumentException("idleTimeout cannot be negative");
}
else if (idleTimeout < TimeUnit.SECONDS.toMillis(30) && idleTimeout != 0) {
logger.warn("idleTimeout is less than 30000ms, did you specify the wrong time unit? Using default instead");
idleTimeout = IDLE_TIMEOUT;
}
if (leakDetectionThreshold != 0 && leakDetectionThreshold < TimeUnit.SECONDS.toMillis(10)) {
logger.warn("leakDetectionThreshold is less than 10000ms, did you specify the wrong time unit? Disabling leak detection");
leakDetectionThreshold = 0;
}
if (maxLifetime < 0) {
logger.error("maxLifetime cannot be negative.");
throw new IllegalArgumentException("maxLifetime cannot be negative.");
}
else if (maxLifetime < TimeUnit.SECONDS.toMillis(120) && maxLifetime != 0) {
logger.warn("maxLifetime is less than 120000ms, did you specify the wrong time unit? Using default instead.");
maxLifetime = MAX_LIFETIME;
}
}
private void logConfiguration()
{
LOGGER.debug("HikariCP pool {} configuration:", poolName);
Set<String> propertyNames = new TreeSet<String>(PropertyBeanSetter.getPropertyNames(HikariConfig.class));
for (String prop : propertyNames) {
try {
Object value = PropertyBeanSetter.getProperty(prop, this);
prop = (prop + "................................................").substring(0, 32);
LOGGER.debug(prop + (value != null ? value : ""));
}
catch (Exception e) {
continue;
}
}
}
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);
}
}
}
}
}

@ -0,0 +1,145 @@
/*
* 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;
/**
* The javax.management MBean for a Hikiri pool configuration.
*
* @author Brett Wooldridge
*/
public interface HikariConfigMBean
{
/**
* Get the maximum number of milliseconds that a client will wait for a connection from the pool. If this
* time is exceeded without a connection becoming available, a SQLException will be thrown from
* {@link javax.sql.DataSource#getConnection()}.
*
* @return the connection timeout in milliseconds
*/
long getConnectionTimeout();
/**
* Set the maximum number of milliseconds that a client will wait for a connection from the pool. If this
* time is exceeded without a connection becoming available, a SQLException will be thrown from
* {@link javax.sql.DataSource#getConnection()}.
*
* @param connectionTimeoutMs the connection timeout in milliseconds
*/
void setConnectionTimeout(long connectionTimeoutMs);
/**
* This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit
* idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30
* seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout.
* A value of 0 means that idle connections are never removed from the pool.
*
* @return the idle timeout in milliseconds
*/
long getIdleTimeout();
/**
* This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit
* idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30
* seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout.
* A value of 0 means that idle connections are never removed from the pool.
*
* @param idleTimeoutMs the idle timeout in milliseconds
*/
void setIdleTimeout(long idleTimeoutMs);
/**
* This property controls the amount of time that a connection can be out of the pool before a message is
* logged indicating a possible connection leak. A value of 0 means leak detection is disabled.
*
* @return the connection leak detection threshold in milliseconds
*/
long getLeakDetectionThreshold();
/**
* This property controls the amount of time that a connection can be out of the pool before a message is
* logged indicating a possible connection leak. A value of 0 means leak detection is disabled.
*
* @param leakDetectionThresholdMs the connection leak detection threshold in milliseconds
*/
void setLeakDetectionThreshold(long leakDetectionThresholdMs);
/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
* retired, only when it is idle will it be removed.
*
* @return the maximum connection lifetime in milliseconds
*/
long getMaxLifetime();
/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
* retired, only when it is idle will it be removed.
*
* @param maxLifetimeMs the maximum connection lifetime in milliseconds
*/
void setMaxLifetime(long maxLifetimeMs);
/**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database
* backend.
* <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out.
*
* @return the minimum number of connections in the pool
*/
int getMinimumIdle();
/**
* The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool,
* including both idle and in-use connections. If the idle connections dip below this value, HikariCP will
* make a best effort to restore them quickly and efficiently.
*
* @param minIdle the minimum number of idle connections in the pool to maintain
*/
void setMinimumIdle(int minIdle);
/**
* The property controls the maximum number of connections that HikariCP will keep in the pool,
* including both idle and in-use connections.
*
* @return the maximum number of connections in the pool
*/
int getMaximumPoolSize();
/**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database
* backend.
* <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out.
*
* @param maxPoolSize the maximum number of connections in the pool
*/
void setMaximumPoolSize(int maxPoolSize);
/**
* The name of the connection pool.
*
* @return the name of the connection pool
*/
String getPoolName();
}

@ -0,0 +1,288 @@
/*
* 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.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.util.DriverDataSource;
/**
* The HikariCP pooled DataSource.
*
* @author Brett Wooldridge
*/
public class HikariDataSource extends HikariConfig implements DataSource
{
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 int loginTimeout;
/* Package scopped for testing */
private final HikariPool fastPathPool;
private volatile HikariPool pool;
/**
* Default constructor. Setters be used to configure the pool. Using
* this constructor vs. {@link #HikariDataSource(HikariConfig)} will
* result in {@link #getConnection()} performance that is slightly lower
* due to lazy initialization checks.
*/
public HikariDataSource()
{
super();
fastPathPool = null;
multiPool = new HashMap<MultiPoolKey, HikariPool>();
}
/**
* Construct a HikariDataSource with the specified configuration.
*
* @param configuration a HikariConfig instance
*/
public HikariDataSource(HikariConfig configuration)
{
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} */
@Override
public Connection getConnection() throws SQLException
{
if (isShutdown) {
throw new SQLException("Pool has been shutdown");
}
if (fastPathPool != null) {
return fastPathPool.getConnection();
}
// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
validate();
LOGGER.info("HikariCP pool {} is starting.", getPoolName());
pool = result = new HikariPool(this);
multiPool.put(new MultiPoolKey(getUsername(), getPassword()), pool);
}
}
}
return result.getConnection();
}
/** {@inheritDoc} */
@Override
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();
}
/** {@inheritDoc} */
@Override
public PrintWriter getLogWriter() throws SQLException
{
return (pool.getDataSource() != null ? pool.getDataSource().getLogWriter() : null);
}
/** {@inheritDoc} */
@Override
public void setLogWriter(PrintWriter out) throws SQLException
{
if (pool.getDataSource() != null) {
pool.getDataSource().setLogWriter(out);
}
}
/** {@inheritDoc} */
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
this.loginTimeout = seconds;
}
/** {@inheritDoc} */
@Override
public int getLoginTimeout() throws SQLException
{
return loginTimeout;
}
/** {@inheritDoc} */
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
{
throw new SQLFeatureNotSupportedException();
}
/** {@inheritDoc} */
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException
{
if (pool != null && iface.isInstance(pool.getDataSource())) {
return (T) pool.getDataSource();
}
throw new SQLException("Wrapped connection is not an instance of " + iface);
}
/** {@inheritDoc} */
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return (pool != null && pool.getDataSource().getClass().isAssignableFrom(iface));
}
/**
* Evict a connection from the pool. Use caution using this method, if you
* evict the same connection more than one time, the internal pool accounting
* will become invalid and the pool may stop functioning.
*
* @param connection the connection to evict from the pool
*/
public void evictConnection(Connection connection)
{
if (!isShutdown && pool != null && connection instanceof IHikariConnectionProxy) {
pool.closeConnection((IHikariConnectionProxy) connection);
}
}
/**
* <code>close()</code> and <code>shutdown()</code> are synonymous.
*/
public void close()
{
shutdown();
}
/**
* Shutdown the DataSource and its associated pool.
*/
public void shutdown()
{
if (isShutdown) {
return;
}
isShutdown = true;
if (fastPathPool != null) {
shutdownHelper(fastPathPool);
}
for (HikariPool hikariPool : multiPool.values()) {
shutdownHelper(hikariPool);
}
}
/** {@inheritDoc} */
@Override
public String toString()
{
return String.format("HikariDataSource (%s)", pool);
}
private void shutdownHelper(HikariPool hPool)
{
try {
hPool.shutdown();
}
catch (InterruptedException e) {
LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e);
}
if (hPool.getDataSource() instanceof DriverDataSource) {
((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 != otherKey.username) {
return false;
}
else if (password != null && !password.equals(otherKey.password)) {
return false;
}
else if (password != otherKey.password) {
return false;
}
return true;
}
}
}

@ -0,0 +1,108 @@
/*
* 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.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import javax.sql.DataSource;
import com.zaxxer.hikari.util.PropertyBeanSetter;
/**
* A JNDI factory that produces HikariDataSource instances.
*
* @author Brett Wooldridge
*/
public class HikariJNDIFactory implements ObjectFactory
{
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception
{
// We only know how to deal with <code>javax.naming.Reference</code> that specify a class name of "javax.sql.DataSource"
if ((obj == null) || !(obj instanceof Reference)) {
return null;
}
Reference ref = (Reference) obj;
if (!"javax.sql.DataSource".equals(ref.getClassName())) {
throw new NamingException(ref.getClassName() + " is not a valid class name/type for this JNDI factory.");
}
Properties properties = new Properties();
for (String propertyName : PropertyBeanSetter.getPropertyNames(HikariConfig.class)) {
RefAddr ra = ref.get(propertyName);
if (ra != null) {
String propertyValue = ra.getContent().toString();
properties.setProperty(propertyName, propertyValue);
}
}
return createDataSource(properties, nameCtx);
}
private DataSource createDataSource(Properties properties, Context context)
{
if (properties.getProperty("dataSourceJNDI") != null) {
return lookupJndiDataSource(properties, context);
}
return new HikariDataSource(new HikariConfig(properties));
}
private DataSource lookupJndiDataSource(Properties properties, Context context)
{
DataSource jndiDS = null;
String jndiName = properties.getProperty("dataSourceJNDI");
try {
if (context != null) {
jndiDS = (DataSource) context.lookup(jndiName);
}
else {
throw new RuntimeException("dataSourceJNDI property is configued, but local JNDI context is null.");
}
}
catch (NamingException e) {
throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the local context.");
}
if (jndiDS == null) {
try {
context = (Context) (new InitialContext());
jndiDS = (DataSource) context.lookup(jndiName);
}
catch (NamingException e) {
throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the InitialContext.");
}
}
if (jndiDS != null) {
HikariConfig config = new HikariConfig(properties);
config.setDataSource(jndiDS);
return new HikariDataSource(config);
}
return null;
}
}

@ -0,0 +1,41 @@
/*
* 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;
}

@ -0,0 +1,70 @@
/*
* 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.hibernate;
import java.util.Map;
import java.util.Properties;
import org.hibernate.cfg.AvailableSettings;
import com.zaxxer.hikari.HikariConfig;
/**
* Utility class to map Hibernate properties to HikariCP configuration properties.
*
* @author Brett Wooldridge, Luca Burgazzoli
*/
public class HikariConfigurationUtil
{
public static final String CONFIG_PREFIX = "hibernate.hikari.";
public static final String CONFIG_PREFIX_DATASOURCE = "hibernate.hikari.dataSource.";
/**
* Create/load a HikariConfig from Hibernate properties.
*
* @param props a map of Hibernate properties
* @return a HikariConfig
*/
@SuppressWarnings("rawtypes")
public static HikariConfig loadConfiguration(Map props)
{
Properties hikariProps = new Properties();
copyProperty(AvailableSettings.ISOLATION, props, "transactionIsolation", hikariProps);
copyProperty(AvailableSettings.AUTOCOMMIT, props, "autoCommit", hikariProps);
copyProperty(AvailableSettings.DRIVER, props, "driverClassName", hikariProps);
copyProperty(AvailableSettings.URL, props, "jdbcUrl", hikariProps);
copyProperty(AvailableSettings.USER, props, "username", hikariProps);
copyProperty(AvailableSettings.PASS, props, "password", hikariProps);
for (Object keyo : props.keySet()) {
String key = (String) keyo;
if (key.startsWith(CONFIG_PREFIX)) {
hikariProps.setProperty(key.substring(CONFIG_PREFIX.length()), (String) props.get(key));
}
}
return new HikariConfig(hikariProps);
}
@SuppressWarnings("rawtypes")
private static void copyProperty(String srcKey, Map src, String dstKey, Properties dst)
{
if (src.containsKey(srcKey)) {
dst.setProperty(dstKey, (String) src.get(srcKey));
}
}
}

@ -0,0 +1,145 @@
/*
* 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.hibernate;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.UnknownUnwrapTypeException;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Stoppable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
/**
* Connection provider for Hibernate 4.3.
*
* @author Brett Wooldridge, Luca Burgazzoli
*/
public class HikariConnectionProvider implements ConnectionProvider, Configurable, Stoppable
{
private static final long serialVersionUID = -9131625057941275711L;
private static final Logger LOGGER = LoggerFactory.getLogger(HikariConnectionProvider.class);
/**
* HikariCP configuration.
*/
private HikariConfig hcfg;
/**
* HikariCP data source.
*/
private HikariDataSource hds;
// *************************************************************************
//
// *************************************************************************
/**
* c-tor
*/
public HikariConnectionProvider()
{
this.hcfg = null;
this.hds = null;
}
// *************************************************************************
// Configurable
// *************************************************************************
@SuppressWarnings("rawtypes")
@Override
public void configure(Map props) throws HibernateException
{
try {
LOGGER.debug("Configuring HikariCP");
this.hcfg = HikariConfigurationUtil.loadConfiguration(props);
this.hds = new HikariDataSource(this.hcfg);
}
catch (Exception e) {
throw new HibernateException(e);
}
LOGGER.debug("HikariCP Configured");
}
// *************************************************************************
// ConnectionProvider
// *************************************************************************
@Override
public Connection getConnection() throws SQLException
{
Connection conn = null;
if (this.hds != null) {
conn = this.hds.getConnection();
}
return conn;
}
@Override
public void closeConnection(Connection conn) throws SQLException
{
conn.close();
}
@Override
public boolean supportsAggressiveRelease()
{
return false;
}
@Override
@SuppressWarnings("rawtypes")
public boolean isUnwrappableAs(Class unwrapType)
{
return ConnectionProvider.class.equals(unwrapType) || HikariConnectionProvider.class.isAssignableFrom(unwrapType);
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> unwrapType)
{
if (isUnwrappableAs(unwrapType)) {
return (T) this;
}
else {
throw new UnknownUnwrapTypeException(unwrapType);
}
}
// *************************************************************************
// Stoppable
// *************************************************************************
@Override
public void stop()
{
this.hds.shutdown();
}
}

@ -0,0 +1,65 @@
/*
* Copyright (C) 2013,2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.metrics;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.zaxxer.hikari.pool.HikariPool;
public final class CodaHaleMetricsTracker extends MetricsTracker
{
private MetricRegistry registry;
private Timer connectionObtainTimer;
private Histogram connectionUsage;
public CodaHaleMetricsTracker(String poolName)
{
registry = new MetricRegistry();
connectionObtainTimer = registry.timer(MetricRegistry.name(HikariPool.class, "connection", "wait"));
connectionUsage = registry.histogram(MetricRegistry.name(HikariPool.class, "connection", "usage"));
}
@Override
public Context recordConnectionRequest(long requestTime)
{
return new Context(connectionObtainTimer);
}
@Override
public void recordConnectionUsage(long usageMilleseconds)
{
connectionUsage.update(usageMilleseconds);
}
public static final class Context extends MetricsContext
{
Timer.Context innerContext;
Context(Timer timer)
{
innerContext = timer.time();
}
public void stop()
{
if (innerContext != null) {
innerContext.stop();
}
}
}
}

@ -0,0 +1,57 @@
/*
* Copyright (C) 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.metrics;
/**
*
* @author Brett Wooldridge
*/
public interface IMetricsTracker
{
/**
* This method is called when a connection request starts. The {@link MetricsContext#stop()}
* method will be called at the completion of the connection request, whether or not an
* exception occurred.
*
* @param startTime the timestamp of the start time as returned by System.currentTimeMillis()
* @return an instance of MetricsContext
*/
public MetricsContext recordConnectionRequest(long startTime);
/**
* This method is called when a Connection is closed, with the total time in milliseconds
* that the Connection was out of the pool.
*
* @param usageMilleseconds the Connection usage time in milliseconds
*/
public void recordConnectionUsage(long usageMilleseconds);
/**
* A base instance of a MetricsContext. Classes extending this class should exhibit the
* behavior of "starting" a timer upon contruction, and "stopping" the timer when the
* {@link MetricsContext#stop()} method is called.
*
* @author Brett Wooldridge
*/
public static class MetricsContext
{
public void stop()
{
// do nothing
}
}
}

@ -0,0 +1,36 @@
/*
* Copyright (C) 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.metrics;
import com.zaxxer.hikari.util.PoolUtilities;
/**
*
* @author Brett Wooldridge
*/
public final class MetricsFactory
{
private MetricsFactory()
{
// private contructor
}
public static final IMetricsTracker createMetricsTracker(String metricsClassName, String poolName)
{
return PoolUtilities.createInstance(metricsClassName, IMetricsTracker.class, poolName);
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) 2013,2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.metrics;
/**
* This class does absolutely nothing.
*
* @author Brett Wooldridge
*/
public class MetricsTracker implements IMetricsTracker
{
public static final MetricsContext NO_CONTEXT = new MetricsContext();
public MetricsTracker()
{
}
public MetricsTracker(String poolName)
{
}
public MetricsContext recordConnectionRequest(long requestTime)
{
return NO_CONTEXT;
}
public void recordConnectionUsage(long usageMilleseconds)
{
}
}

@ -0,0 +1,94 @@
/*
* 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.pool;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
/**
* Helper class to register our MBeans.
*
* @author Brett Wooldridge
*/
public final class HikariMBeanElf
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariMBeanElf.class);
private HikariMBeanElf()
{
// utility class
}
/**
* Register MBeans for HikariConfig and HikariPool.
*
* @param configuration a HikariConfig instance
* @param pool a HikariPool instance
*/
public static void registerMBeans(HikariConfig configuration, HikariPool pool)
{
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")");
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")");
if (!mBeanServer.isRegistered(poolConfigName)) {
mBeanServer.registerMBean(configuration, poolConfigName);
mBeanServer.registerMBean(pool, poolName);
}
else {
LOGGER.error("You cannot use the same HikariConfig for separate pool instances.");
}
}
catch (Exception e) {
LOGGER.warn("Unable to register management beans.", e);
}
}
/**
* Unregister MBeans for HikariConfig and HikariPool.
*
* @param configuration a HikariConfig instance
* @param pool a HikariPool instance
*/
public static void unregisterMBeans(HikariConfig configuration, HikariPool pool)
{
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")");
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")");
if (mBeanServer.isRegistered(poolConfigName)) {
mBeanServer.unregisterMBean(poolConfigName);
mBeanServer.unregisterMBean(poolName);
}
else {
LOGGER.error("No registered MBean for {}.", configuration.getPoolName());
}
}
catch (Exception e) {
LOGGER.warn("Unable to unregister management beans.", e);
}
}
}

@ -0,0 +1,554 @@
/*
* 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.pool;
import static com.zaxxer.hikari.util.PoolUtilities.createInstance;
import static com.zaxxer.hikari.util.PoolUtilities.createThreadPoolExecutor;
import static com.zaxxer.hikari.util.PoolUtilities.elapsedTimeMs;
import static com.zaxxer.hikari.util.PoolUtilities.executeSqlAutoCommit;
import static com.zaxxer.hikari.util.PoolUtilities.quietlyCloseConnection;
import static com.zaxxer.hikari.util.PoolUtilities.quietlySleep;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.IConnectionCustomizer;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.IMetricsTracker.MetricsContext;
import com.zaxxer.hikari.metrics.MetricsFactory;
import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
import com.zaxxer.hikari.util.DriverDataSource;
import com.zaxxer.hikari.util.PropertyBeanSetter;
/**
* This is the primary connection pool class that provides the basic
* pooling behavior for HikariCP.
*
* @author Brett Wooldridge
*/
public final class HikariPool implements HikariPoolMBean, IBagStateListener
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class);
private final DataSource dataSource;
private final IConnectionCustomizer connectionCustomizer;
private final HikariConfig configuration;
private final ConcurrentBag<IHikariConnectionProxy> connectionBag;
private final ThreadPoolExecutor addConnectionExecutor;
private final IMetricsTracker metricsTracker;
private final AtomicReference<Throwable> lastConnectionFailure;
private final AtomicInteger totalConnections;
private final Timer houseKeepingTimer;
private final boolean isAutoCommit;
private final boolean isIsolateInternalQueries;
private final boolean isReadOnly;
private final boolean isRecordMetrics;
private final boolean isRegisteredMbeans;
private final boolean isJdbc4ConnectionTest;
private final long leakDetectionThreshold;
private final String catalog;
private final String username;
private final String password;
private volatile long connectionTimeout;
private volatile boolean isShutdown;
private int transactionIsolation;
/**
* Construct a HikariPool with the specified configuration.
*
* @param configuration a HikariConfig instance
*/
public HikariPool(HikariConfig configuration)
{
this(configuration, configuration.getUsername(), configuration.getPassword());
}
/**
* Construct a HikariPool with the specified configuration.
*
* @param configuration a HikariConfig instance
* @param username authentication username
* @param password authentication password
*/
public HikariPool(HikariConfig configuration, String username, String password)
{
this.configuration = configuration;
this.username = username;
this.password = password;
this.totalConnections = new AtomicInteger();
this.connectionBag = new ConcurrentBag<IHikariConnectionProxy>();
this.connectionBag.addBagStateListener(this);
this.lastConnectionFailure = new AtomicReference<Throwable>();
this.connectionTimeout = configuration.getConnectionTimeout();
this.catalog = configuration.getCatalog();
this.connectionCustomizer = initializeCustomizer();
this.isAutoCommit = configuration.isAutoCommit();
this.isIsolateInternalQueries = configuration.isIsolateInternalQueries();
this.isReadOnly = configuration.isReadOnly();
this.isRegisteredMbeans = configuration.isRegisterMbeans();
this.isJdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
this.transactionIsolation = configuration.getTransactionIsolation();
this.isRecordMetrics = configuration.isRecordMetrics();
this.metricsTracker = MetricsFactory.createMetricsTracker((isRecordMetrics ? configuration.getMetricsTrackerClassName()
: "com.zaxxer.hikari.metrics.MetricsTracker"), configuration.getPoolName());
this.dataSource = initializeDataSource();
if (isRegisteredMbeans) {
HikariMBeanElf.registerMBeans(configuration, this);
}
addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler");
fillPool();
long delayPeriod = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", TimeUnit.SECONDS.toMillis(30L));
houseKeepingTimer = new Timer("Hikari Housekeeping Timer (pool " + configuration.getPoolName() + ")", true);
houseKeepingTimer.scheduleAtFixedRate(new HouseKeeper(), delayPeriod, delayPeriod);
}
/**
* Get a connection from the pool, or timeout trying.
*
* @return a java.sql.Connection instance
* @throws SQLException thrown if a timeout occurs trying to obtain a connection
*/
public Connection getConnection() throws SQLException
{
final long start = System.currentTimeMillis();
final MetricsContext context = (isRecordMetrics ? metricsTracker.recordConnectionRequest(start) : MetricsTracker.NO_CONTEXT);
long timeout = connectionTimeout;
try {
do {
IHikariConnectionProxy connection = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);
if (connection == null) {
break; // We timed out... break and throw exception
}
final long now = System.currentTimeMillis();
connection.unclose(now);
if (now > connection.getExpirationTime() || (now - connection.getLastAccess() > 1000L && !isConnectionAlive(connection, timeout))) {
closeConnection(connection); // Throw away the dead connection and try again
timeout -= elapsedTimeMs(start);
continue;
}
else if (leakDetectionThreshold != 0) {
connection.captureStack(leakDetectionThreshold, houseKeepingTimer);
}
return connection;
}
while (timeout > 0L);
}
catch (InterruptedException e) {
throw new SQLException("Interrupted during connection acquisition", e);
}
finally {
context.stop();
}
logPoolState("Timeout failure ");
throw new SQLException(String.format("Timeout of %dms encountered waiting for connection.", configuration.getConnectionTimeout()),
lastConnectionFailure.getAndSet(null));
}
/**
* Release a connection back to the pool, or permanently close it if it is broken.
*
* @param connectionProxy the connection to release back to the pool
* @param isBroken true if the connection was detected as broken
*/
public void releaseConnection(final IHikariConnectionProxy connectionProxy, final boolean isBroken)
{
if (isRecordMetrics) {
metricsTracker.recordConnectionUsage(elapsedTimeMs(connectionProxy.getLastOpenTime()));
}
if (isBroken || isShutdown) {
LOGGER.debug("Connection returned to pool {} is broken, or the pool is shutting down. Closing connection.", configuration.getPoolName());
closeConnection(connectionProxy);
return;
}
connectionBag.requite(connectionProxy);
}
/**
* Shutdown the pool, closing all idle connections and aborting or closing
* active connections.
*
* @throws InterruptedException thrown if the thread is interrupted during shutdown
*/
public void shutdown() throws InterruptedException
{
if (!isShutdown) {
isShutdown = true;
LOGGER.info("HikariCP pool {} is shutting down.", configuration.getPoolName());
logPoolState("Before shutdown ");
houseKeepingTimer.cancel();
addConnectionExecutor.shutdownNow();
final long start = System.currentTimeMillis();
do {
closeIdleConnections();
abortActiveConnections();
}
while ((getIdleConnections() > 0 || getActiveConnections() > 0) && elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5));
logPoolState("After shutdown ");
if (isRegisteredMbeans) {
HikariMBeanElf.unregisterMBeans(configuration, this);
}
}
}
/**
* Get the wrapped DataSource.
*
* @return the wrapped DataSource
*/
public DataSource getDataSource()
{
return dataSource;
}
/**
* Permanently close the real (underlying) connection (eat any exception).
*
* @param connectionProxy the connection to actually close
*/
public void closeConnection(final IHikariConnectionProxy connectionProxy)
{
try {
int tc = totalConnections.decrementAndGet();
if (tc < 0) {
LOGGER.warn("Internal accounting inconsistency, totalConnections={}", tc, new Exception());
}
connectionProxy.realClose();
}
catch (SQLException e) {
return;
}
finally {
connectionBag.remove(connectionProxy);
}
}
@Override
public String toString()
{
return configuration.getPoolName();
}
// ***********************************************************************
// IBagStateListener callback
// ***********************************************************************
/** {@inheritDoc} */
@Override
public void addBagItem()
{
addConnectionExecutor.submit( () -> {
long sleepBackoff = 200L;
final int maxPoolSize = configuration.getMaximumPoolSize();
final int minIdle = configuration.getMinimumIdle();
while (!isShutdown && totalConnections.get() < maxPoolSize && (minIdle == 0 || getIdleConnections() < minIdle)) {
if (!addConnection()) {
quietlySleep(sleepBackoff);
sleepBackoff = Math.min(1000L, (long) ((double) sleepBackoff * 1.5));
continue;
}
if (minIdle == 0) {
break; // This break is here so we only add one connection when there is no min. idle
}
}
});
}
// ***********************************************************************
// HikariPoolMBean methods
// ***********************************************************************
/** {@inheritDoc} */
@Override
public int getActiveConnections()
{
return Math.min(configuration.getMaximumPoolSize(), totalConnections.get() - getIdleConnections());
}
/** {@inheritDoc} */
@Override
public int getIdleConnections()
{
return (int) connectionBag.getCount(ConcurrentBag.STATE_NOT_IN_USE);
}
/** {@inheritDoc} */
@Override
public int getTotalConnections()
{
return totalConnections.get();
}
/** {@inheritDoc} */
@Override
public int getThreadsAwaitingConnection()
{
return connectionBag.getPendingQueue();
}
/** {@inheritDoc} */
@Override
public void closeIdleConnections()
{
connectionBag.values(ConcurrentBag.STATE_NOT_IN_USE).forEach(connectionProxy -> {
if (connectionBag.reserve(connectionProxy)) {
closeConnection(connectionProxy);
}
});
}
// ***********************************************************************
// Private methods
// ***********************************************************************
/**
* Create and add a single connection to the pool.
*/
private boolean addConnection()
{
Connection connection = null;
try {
// Speculative increment of totalConnections with expectation of success
if (totalConnections.incrementAndGet() > configuration.getMaximumPoolSize() || isShutdown) {
totalConnections.decrementAndGet();
return true;
}
connection = (username == null && password == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);
transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation);
connectionCustomizer.customize(connection);
executeSqlAutoCommit(connection, configuration.getConnectionInitSql());
IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, connection, configuration.getMaxLifetime(), transactionIsolation,
isAutoCommit, isReadOnly, catalog);
proxyConnection.resetConnectionState();
connectionBag.add(proxyConnection);
lastConnectionFailure.set(null);
return true;
}
catch (Exception e) {
// We failed, so undo speculative increment of totalConnections
totalConnections.decrementAndGet();
lastConnectionFailure.set(e);
quietlyCloseConnection(connection);
LOGGER.debug("Connection attempt to database {} failed: {}", configuration.getPoolName(), e.getMessage(), e);
return false;
}
}
/**
* Check whether the connection is alive or not.
*
* @param connection the connection to test
* @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 IHikariConnectionProxy connection, long timeoutMs)
{
try {
final boolean timeoutEnabled = (configuration.getConnectionTimeout() != Integer.MAX_VALUE);
timeoutMs = timeoutEnabled ? Math.max(1000L, timeoutMs) : 0L;
if (isJdbc4ConnectionTest) {
return connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs));
}
try (Statement statement = connection.createStatement()) {
statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs));
statement.executeQuery(configuration.getConnectionTestQuery());
return true;
}
finally {
if (isIsolateInternalQueries && !isAutoCommit) {
connection.rollback();
}
}
}
catch (SQLException e) {
LOGGER.warn("Exception during keep alive check, that means the connection must be dead.", e);
return false;
}
}
/**
* Fill the pool up to the minimum size.
*/
private void fillPool()
{
if (configuration.isInitializationFailFast()) {
for (int maxIters = configuration.getMinimumIdle(); maxIters > 0; maxIters--) {
if (!addConnection()) {
throw new RuntimeException("Fail-fast during pool initialization", lastConnectionFailure.getAndSet(null));
}
}
}
else if (configuration.getMinimumIdle() > 0) {
addBagItem();
}
}
/**
* Attempt to abort() active connections on Java7+, or close() them on Java6.
*
* @throws InterruptedException
*/
private void abortActiveConnections() throws InterruptedException
{
ThreadPoolExecutor assassinExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection assassin");
connectionBag.values(ConcurrentBag.STATE_IN_USE).parallelStream().forEach(connectionProxy -> {
try {
connectionProxy.abort(assassinExecutor);
}
catch (AbstractMethodError ame)
{
try {
connectionProxy.close();
}
catch (Exception e) {
return;
}
}
catch (SQLException e) {
return;
}
finally {
totalConnections.decrementAndGet();
try {
connectionBag.remove(connectionProxy);
}
catch (IllegalStateException ise) {
return;
}
}
});
assassinExecutor.shutdown();
assassinExecutor.awaitTermination(5L, TimeUnit.SECONDS);
}
/**
* Create/initialize the underlying DataSource.
*
* @return the DataSource
*/
private DataSource initializeDataSource()
{
String dsClassName = configuration.getDataSourceClassName();
if (configuration.getDataSource() == null && dsClassName != null) {
DataSource dataSource = createInstance(dsClassName, DataSource.class);
PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties());
return dataSource;
}
else if (configuration.getJdbcUrl() != null) {
return new DriverDataSource(configuration.getJdbcUrl(), configuration.getDataSourceProperties(), username, password);
}
return configuration.getDataSource();
}
private IConnectionCustomizer initializeCustomizer()
{
if (configuration.getConnectionCustomizerClassName() != null) {
return createInstance(configuration.getConnectionCustomizerClassName(), IConnectionCustomizer.class);
}
return configuration.getConnectionCustomizer();
}
private void logPoolState(String... prefix)
{
int total = totalConnections.get();
int idle = getIdleConnections();
LOGGER.debug("{}pool stats {} (total={}, inUse={}, avail={}, waiting={})", (prefix.length > 0 ? prefix[0] : ""), configuration.getPoolName(), total,
total - idle, idle, getThreadsAwaitingConnection());
}
/**
* The house keeping task to retire idle and maxAge connections.
*/
private class HouseKeeper extends TimerTask
{
@Override
public void run()
{
logPoolState("Before cleanup ");
connectionTimeout = configuration.getConnectionTimeout(); // refresh member in case it changed
houseKeepingTimer.purge(); // purge cancelled timers
final long now = System.currentTimeMillis();
final long idleTimeout = configuration.getIdleTimeout();
connectionBag.values(ConcurrentBag.STATE_NOT_IN_USE).forEach(connectionProxy -> {
if (connectionBag.reserve(connectionProxy)) {
if ((idleTimeout > 0L && now > connectionProxy.getLastAccess() + idleTimeout) || (now > connectionProxy.getExpirationTime())) {
closeConnection(connectionProxy);
return;
}
connectionBag.unreserve(connectionProxy);
}
});
logPoolState("After cleanup ");
addBagItem(); // Try to maintain minimum connections
}
}
}

@ -0,0 +1,35 @@
/*
* 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.pool;
/**
* The javax.management MBean for a Hikiri pool instance.
*
* @author Brett Wooldridge
*/
public interface HikariPoolMBean
{
int getIdleConnections();
int getActiveConnections();
int getTotalConnections();
int getThreadsAwaitingConnection();
void closeIdleConnections();
}

@ -0,0 +1,37 @@
/*
* 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.proxy;
import java.sql.CallableStatement;
/**
* This is the proxy class for java.sql.CallableStatement.
*
* @author Brett Wooldridge
*/
public abstract class CallableStatementProxy extends PreparedStatementProxy implements CallableStatement
{
protected CallableStatementProxy(ConnectionProxy connection, CallableStatement statement)
{
super(connection, statement);
}
// **********************************************************************
// Overridden java.sql.CallableStatement Methods
// **********************************************************************
}

@ -0,0 +1,603 @@
/*
* 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.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.util.FastList;
/**
* This is the proxy class for java.sql.Connection.
*
* @author Brett Wooldridge
*/
public abstract class ConnectionProxy implements IHikariConnectionProxy
{
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionProxy.class);
private static final Set<String> SQL_ERRORS;
protected final Connection delegate;
private final FastList<Statement> openStatements;
private final HikariPool parentPool;
private final AtomicInteger state;
private final String defaultCatalog;
private final long expirationTime;
private final int defaultIsolationLevel;
private final boolean defaultAutoCommit;
private final boolean defaultReadOnly;
private boolean forceClose;
private boolean isAutoCommitDirty;
private boolean isCatalogDirty;
private boolean isClosed;
private boolean isReadOnlyDirty;
private boolean isTransactionIsolationDirty;
private volatile long lastAccess;
private long uncloseTime;
private TimerTask leakTask;
private final int hashCode;
// static initializer
static {
SQL_ERRORS = new HashSet<String>();
SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN
SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN
SQL_ERRORS.add("57P03"); // CANNOT CONNECT NOW
SQL_ERRORS.add("01002"); // SQL92 disconnect error
SQL_ERRORS.add("JZ0C0"); // Sybase disconnect error
SQL_ERRORS.add("JZ0C1"); // Sybase disconnect error
}
protected ConnectionProxy(HikariPool pool, Connection connection, long maxLifetime, int defaultIsolationLevel, boolean defaultAutoCommit,
boolean defaultReadOnly, String defaultCatalog)
{
this.parentPool = pool;
this.delegate = connection;
this.defaultIsolationLevel = defaultIsolationLevel;
this.defaultAutoCommit = defaultAutoCommit;
this.defaultReadOnly = defaultReadOnly;
this.defaultCatalog = defaultCatalog;
this.state = new AtomicInteger();
long now = System.currentTimeMillis();
this.expirationTime = (maxLifetime > 0 ? now + maxLifetime : Long.MAX_VALUE);
this.lastAccess = now;
this.openStatements = new FastList<Statement>(Statement.class);
this.hashCode = System.identityHashCode(this);
isCatalogDirty = true;
isReadOnlyDirty = defaultReadOnly;
isAutoCommitDirty = true;
isTransactionIsolationDirty = true;
}
/** {@inheritDoc} */
@Override
public final boolean equals(Object other)
{
return this == other;
}
/** {@inheritDoc} */
@Override
public final int hashCode()
{
return hashCode;
}
// ***********************************************************************
// IHikariConnectionProxy methods
// ***********************************************************************
/** {@inheritDoc} */
@Override
public final void captureStack(long leakDetectionThreshold, Timer scheduler)
{
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
StackTraceElement[] leakTrace = new StackTraceElement[trace.length - 4];
System.arraycopy(trace, 4, leakTrace, 0, leakTrace.length);
leakTask = new LeakTask(leakTrace, leakDetectionThreshold);
scheduler.schedule(leakTask, leakDetectionThreshold);
}
/** {@inheritDoc} */
@Override
public final void checkException(SQLException sqle)
{
String sqlState = sqle.getSQLState();
if (sqlState != null) {
forceClose |= sqlState.startsWith("08") | SQL_ERRORS.contains(sqlState);
if (forceClose) {
LOGGER.warn(String.format("Connection %s (%s) marked as broken because of SQLSTATE(%s), ErrorCode(%d).", delegate.toString(),
parentPool.toString(), sqlState, sqle.getErrorCode()), sqle);
}
else if (sqle.getNextException() instanceof SQLException) {
checkException(sqle.getNextException());
}
}
}
/** {@inheritDoc} */
@Override
public final long getExpirationTime()
{
return expirationTime;
}
/** {@inheritDoc} */
@Override
public final long getLastAccess()
{
return lastAccess;
}
/** {@inheritDoc} */
@Override
public long getLastOpenTime()
{
return uncloseTime;
}
/** {@inheritDoc} */
@Override
public final boolean isBrokenConnection()
{
return forceClose;
}
/** {@inheritDoc} */
@Override
public final void realClose() throws SQLException
{
delegate.close();
}
/** {@inheritDoc} */
@Override
public final void resetConnectionState() throws SQLException
{
if (!delegate.getAutoCommit()) {
delegate.rollback();
}
if (isReadOnlyDirty) {
delegate.setReadOnly(defaultReadOnly);
isReadOnlyDirty = false;
}
if (isAutoCommitDirty) {
delegate.setAutoCommit(defaultAutoCommit);
isAutoCommitDirty = false;
}
if (isTransactionIsolationDirty) {
delegate.setTransactionIsolation(defaultIsolationLevel);
isTransactionIsolationDirty = false;
}
if (isCatalogDirty && defaultCatalog != null) {
delegate.setCatalog(defaultCatalog);
isCatalogDirty = false;
}
delegate.clearWarnings();
}
/** {@inheritDoc} */
@Override
public final void unclose(final long now)
{
isClosed = false;
uncloseTime = now;
}
/** {@inheritDoc} */
@Override
public final void untrackStatement(Statement statement)
{
// If the connection is not closed. If it is closed, it means this is being
// called back as a result of the close() method below in which case we
// will clear the openStatements collection en mass.
if (!isClosed) {
openStatements.remove(statement);
}
}
// ***********************************************************************
// Internal methods
// ***********************************************************************
protected final void checkClosed() throws SQLException
{
if (isClosed) {
throw new SQLException("Connection is closed");
}
}
private <T extends Statement> T trackStatement(T statement)
{
openStatements.add(statement);
return statement;
}
// **********************************************************************
// IBagManagable Methods
// **********************************************************************
/** {@inheritDoc} */
@Override
public final int getState()
{
return state.get();
}
/** {@inheritDoc} */
@Override
public final boolean compareAndSetState(int expectedState, int newState)
{
return state.compareAndSet(expectedState, newState);
}
// **********************************************************************
// "Overridden" java.sql.Connection Methods
// **********************************************************************
/** {@inheritDoc} */
@Override
public final void close() throws SQLException
{
if (!isClosed) {
isClosed = true;
if (leakTask != null) {
leakTask.cancel();
leakTask = null;
}
try {
final int size = openStatements.size();
if (size > 0) {
for (int i = 0; i < size; i++) {
try {
openStatements.get(i).close();
}
catch (SQLException e) {
checkException(e);
}
}
openStatements.clear();
}
resetConnectionState();
}
catch (SQLException e) {
checkException(e);
throw e;
}
finally {
lastAccess = System.currentTimeMillis();
parentPool.releaseConnection(this, forceClose);
}
}
}
/** {@inheritDoc} */
@Override
public final boolean isClosed() throws SQLException
{
return isClosed;
}
/** {@inheritDoc} */
@Override
public final Statement createStatement() throws SQLException
{
checkClosed();
try {
Statement proxyStatement = ProxyFactory.getProxyStatement(this, delegate.createStatement());
return trackStatement(proxyStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
try {
Statement proxyStatement = ProxyFactory.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency));
return trackStatement(proxyStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
try {
Statement proxyStatement = ProxyFactory.getProxyStatement(this, delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability));
return trackStatement(proxyStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final CallableStatement prepareCall(String sql) throws SQLException
{
checkClosed();
try {
CallableStatement proxyCallableStatement = ProxyFactory.getProxyCallableStatement(this, delegate.prepareCall(sql));
return trackStatement(proxyCallableStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
try {
CallableStatement proxyCallableStatement = ProxyFactory.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency));
return trackStatement(proxyCallableStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
try {
CallableStatement proxyCallableStatement = ProxyFactory.getProxyCallableStatement(this, delegate.prepareCall(sql, resultSetType, resultSetConcurrency,
resultSetHoldability));
return trackStatement(proxyCallableStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql) throws SQLException
{
checkClosed();
try {
PreparedStatement proxyPreparedStatement = ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql));
return trackStatement(proxyPreparedStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
{
checkClosed();
try {
PreparedStatement proxyPreparedStatement = ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, autoGeneratedKeys));
return trackStatement(proxyPreparedStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
try {
PreparedStatement proxyPreparedStatement = ProxyFactory.getProxyPreparedStatement(this,
delegate.prepareStatement(sql, resultSetType, resultSetConcurrency));
return trackStatement(proxyPreparedStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
try {
PreparedStatement proxyPreparedStatement = ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, resultSetType,
resultSetConcurrency,
resultSetHoldability));
return trackStatement(proxyPreparedStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException
{
checkClosed();
try {
PreparedStatement proxyPreparedStatement = ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnIndexes));
return trackStatement(proxyPreparedStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
{
checkClosed();
try {
PreparedStatement proxyPreparedStatement = ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
return trackStatement(proxyPreparedStatement);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final boolean isValid(int timeout) throws SQLException
{
if (isClosed) {
return false;
}
try {
return delegate.isValid(timeout);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final void setAutoCommit(boolean autoCommit) throws SQLException
{
checkClosed();
try {
delegate.setAutoCommit(autoCommit);
isAutoCommitDirty = (autoCommit != defaultAutoCommit);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final void setReadOnly(boolean readOnly) throws SQLException
{
checkClosed();
try {
delegate.setReadOnly(readOnly);
isReadOnlyDirty = (readOnly != defaultReadOnly);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final void setTransactionIsolation(int level) throws SQLException
{
checkClosed();
try {
delegate.setTransactionIsolation(level);
isTransactionIsolationDirty = (level != defaultIsolationLevel);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
@Override
public final void setCatalog(String catalog) throws SQLException
{
checkClosed();
try {
delegate.setCatalog(catalog);
isCatalogDirty = !catalog.equals(defaultCatalog);
}
catch (SQLException e) {
checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final boolean isWrapperFor(Class<?> iface) throws SQLException
{
return iface.isInstance(delegate);
}
/** {@inheritDoc} */
@Override
@SuppressWarnings("unchecked")
public final <T> T unwrap(Class<T> iface) throws SQLException
{
if (iface.isInstance(delegate)) {
return (T) delegate;
}
throw new SQLException("Wrapped connection is not an instance of " + iface);
}
}

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

@ -0,0 +1,210 @@
/*
* 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.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.HashSet;
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 org.slf4j.LoggerFactory;
import com.zaxxer.hikari.util.ClassLoaderUtils;
/**
* This class generates the proxy objects for {@link Connection}, {@link Statement},
* {@link PreparedStatement}, and {@link CallableStatement}. Additionally it injects
* method bodies into the {@link ProxyFactory} class methods that can instantiate
* instances of the generated proxies.
*
* @author Brett Wooldridge
*/
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) {
LoggerFactory.getLogger(JavassistProxyFactory.class).error("Fatal exception during proxy generation", e);
throw new RuntimeException(e);
}
finally {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
/**
* Simply invoking this method causes the initialization of this class. All work
* by this class is performed in static initialization.
*/
public static void initialize()
{
// no-op
}
private JavassistProxyFactory()
{
classPool = new ClassPool();
classPool.importPackage("java.sql");
classPool.appendClassPath(new LoaderClassPath(this.getClass().getClassLoader()));
try {
// Connection is special, it has a checkClosed() call at the beginning
String methodBody = "{ checkClosed(); try { return delegate.method($$); } catch (SQLException e) { checkException(e); throw e;} }";
generateProxyClass(Connection.class, ConnectionProxy.class, methodBody);
// Cast is not needed for these
methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { checkException(e); throw e;} }";
generateProxyClass(Statement.class, StatementProxy.class, methodBody);
// For these we have to cast the delegate
methodBody = "{ try { return ((cast) delegate).method($$); } catch (SQLException e) { checkException(e); throw e;} }";
generateProxyClass(PreparedStatement.class, PreparedStatementProxy.class, methodBody);
generateProxyClass(CallableStatement.class, CallableStatementProxy.class, methodBody);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
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()) {
String methodName = method.getName();
if ("getProxyConnection".equals(methodName)) {
method.setBody("{return new " + packageName + ".ConnectionJavassistProxy($$);}");
}
else if ("getProxyStatement".equals(methodName)) {
method.setBody("{return new " + packageName + ".StatementJavassistProxy($$);}");
}
else if ("getProxyPreparedStatement".equals(methodName)) {
method.setBody("{return new " + packageName + ".PreparedStatementJavassistProxy($$);}");
}
else if ("getProxyCallableStatement".equals(methodName)) {
method.setBody("{return new " + packageName + ".CallableStatementJavassistProxy($$);}");
}
}
proxyCt.toClass(classPool.getClassLoader(), null);
}
/**
* 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.ABSTRACT) != Modifier.ABSTRACT) {
superSigs.add(method.getName() + method.getSignature());
}
}
methodBody = methodBody.replace("cast", primaryInterface.getName());
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;
}
// Track what methods we've added
methods.add(signature);
// Clone the method we want to inject into
CtMethod method = CtNewMethod.copy(intfMethod, targetCt, null);
// Generate a method that simply invokes the same method on the delegate
String modifiedBody;
if (isThrowsSqlException(intfMethod)) {
modifiedBody = methodBody.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);
}
}
if (LoggerFactory.getLogger(getClass()).isDebugEnabled()) {
targetCt.debugWriteFile(System.getProperty("java.io.tmpdir"));
}
return targetCt.toClass(classPool.getClassLoader(), null);
}
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;
}
}

@ -0,0 +1,58 @@
/*
* 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.proxy;
import java.util.TimerTask;
import org.slf4j.LoggerFactory;
/**
* @author Brett Wooldridge
*/
class LeakTask extends TimerTask
{
private final long leakTime;
private StackTraceElement[] stackTrace;
public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold)
{
this.stackTrace = stackTrace;
this.leakTime = System.currentTimeMillis() + leakDetectionThreshold;
}
/** {@inheritDoc} */
@Override
public void run()
{
if (System.currentTimeMillis() > leakTime) {
Exception e = new Exception();
e.setStackTrace(stackTrace);
LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e);
stackTrace = null;
}
}
@Override
public boolean cancel()
{
boolean cancelled = super.cancel();
if (cancelled) {
stackTrace = null;
}
return cancelled;
}
}

@ -0,0 +1,51 @@
/*
* 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.proxy;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* This is the proxy class for java.sql.PreparedStatement.
*
* @author Brett Wooldridge
*/
public abstract class PreparedStatementProxy extends StatementProxy implements PreparedStatement
{
protected PreparedStatementProxy(ConnectionProxy connection, PreparedStatement statement)
{
super(connection, statement);
}
// **********************************************************************
// Overridden java.sql.PreparedStatement Methods
// **********************************************************************
/** {@inheritDoc} */
@Override
public final ResultSet executeQuery() throws SQLException
{
try {
return ((PreparedStatement) delegate).executeQuery();
}
catch (SQLException e) {
connection.checkException(e);
throw e;
}
}
}

@ -0,0 +1,75 @@
/*
* 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.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import com.zaxxer.hikari.pool.HikariPool;
/**
* A factory class that produces proxies around instances of the standard
* JDBC interfaces.
*
* @author Brett Wooldridge
*/
public final class ProxyFactory
{
private ProxyFactory()
{
// unconstructable
}
/**
* Create a proxy for the specified {@link Connection} instance.
*
* @param pool the {@link HikariPool} that will own this proxy
* @param connection the {@link Connection} that will be wrapped by this proxy
* @param maxLifeTime the lifetime of the connection
* @param defaultIsolationLevel the default transaction isolation level of the underlying {@link Connection}
* @param defaultAutoCommit the default auto-commit state of the underlying {@link Connection}
* @param defaultIReadOnly the default readOnly state of the underlying {@link Connection}
* @param defaultCatalog the default catalog of the underlying {@link Connection}
* @return a proxy that wraps the specified {@link Connection}
*/
public static IHikariConnectionProxy getProxyConnection(HikariPool pool, Connection connection, long maxLifeTime, int defaultIsolationLevel,
boolean defaultAutoCommit, boolean defaultIReadOnly, String defaultCatalog)
{
// Body is injected by JavassistProxyFactory
return null;
}
static Statement getProxyStatement(ConnectionProxy connection, Statement statement)
{
// Body is injected by JavassistProxyFactory
return null;
}
static CallableStatement getProxyCallableStatement(ConnectionProxy connection, CallableStatement statement)
{
// Body is injected by JavassistProxyFactory
return null;
}
static PreparedStatement getProxyPreparedStatement(ConnectionProxy connection, PreparedStatement statement)
{
// Body is injected by JavassistProxyFactory
return null;
}
}

@ -0,0 +1,127 @@
/*
* 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.proxy;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* This is the proxy class for java.sql.Statement.
*
* @author Brett Wooldridge
*/
public abstract class StatementProxy implements Statement
{
protected final IHikariConnectionProxy connection;
protected final Statement delegate;
private boolean isClosed;
protected StatementProxy(IHikariConnectionProxy connection, Statement statement)
{
this.connection = connection;
this.delegate = statement;
}
protected final void checkException(SQLException e)
{
connection.checkException(e);
}
// **********************************************************************
// Overridden java.sql.Statement Methods
// **********************************************************************
/** {@inheritDoc} */
@Override
public final void close() throws SQLException
{
if (isClosed) {
return;
}
isClosed = true;
connection.untrackStatement(this);
try {
delegate.close();
}
catch (SQLException e) {
connection.checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final ResultSet executeQuery(String sql) throws SQLException
{
try {
return delegate.executeQuery(sql);
}
catch (SQLException e) {
connection.checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final ResultSet getResultSet() throws SQLException
{
try {
return delegate.getResultSet();
}
catch (SQLException e) {
connection.checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final ResultSet getGeneratedKeys() throws SQLException
{
try {
return delegate.getGeneratedKeys();
}
catch (SQLException e) {
connection.checkException(e);
throw e;
}
}
/** {@inheritDoc} */
@Override
public final Connection getConnection() throws SQLException
{
return connection;
}
@Override
@SuppressWarnings("unchecked")
public final <T> T unwrap(Class<T> iface) throws SQLException
{
if (iface.isInstance(delegate)) {
return (T) delegate;
}
throw new SQLException("Wrapped connection is not an instance of " + iface);
}
}

@ -0,0 +1,93 @@
/*
* 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.util;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author Brett Wooldridge
*/
public final class ClassLoaderUtils
{
/**
* Get the class loader which can be used to generate proxies without leaking memory.
* @return the class loader which can be used to generate proxies without leaking memory.
*/
public static ClassLoader getClassLoader()
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != null) {
return new CascadingClassLoader(cl);
}
return ClassLoaderUtils.class.getClassLoader();
}
public static Class<?> loadClass(String className) throws ClassNotFoundException
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != null) {
return new CascadingClassLoader(cl).loadClass(className);
}
return Class.forName(className);
}
public static Set<Class<?>> getAllInterfaces(Class<?> clazz)
{
Set<Class<?>> interfaces = new HashSet<Class<?>>();
for (Class<?> intf : Arrays.asList(clazz.getInterfaces())) {
if (intf.getInterfaces().length > 0) {
interfaces.addAll(getAllInterfaces(intf));
}
interfaces.add(intf);
}
if (clazz.getSuperclass() != null) {
interfaces.addAll(getAllInterfaces(clazz.getSuperclass()));
}
if (clazz.isInterface()) {
interfaces.add(clazz);
}
return interfaces;
}
private static class CascadingClassLoader extends ClassLoader
{
private ClassLoader contextLoader;
CascadingClassLoader(ClassLoader contextLoader)
{
this.contextLoader = contextLoader;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try {
return contextLoader.loadClass(name);
}
catch (ClassNotFoundException cnfe) {
return CascadingClassLoader.class.getClassLoader().loadClass(name);
}
}
}
}

@ -0,0 +1,315 @@
/*
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.util;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
import java.util.stream.Collectors;
/**
* This is a specialized concurrent bag that achieves superior performance
* to LinkedBlockingQueue and LinkedTransferQueue for the purposes of a
* connection pool. It uses ThreadLocal storage when possible to avoid
* locks, but resorts to scanning a common collection if there are no
* available items in the ThreadLocal list. Not-in-use items in the
* ThreadLocal lists can be "stolen" when the borrowing thread has none
* of its own. It is a "lock-less" implementation using a specialized
* AbstractQueuedLongSynchronizer to manage cross-thread signaling.
*
* Note that items that are "borrowed" from the bag are not actually
* removed from any collection, so garbage collection will not occur
* even if the reference is abandoned. Thus care must be taken to
* "requite" borrowed objects otherwise a memory leak will result. Only
* the "remove" method can completely remove an object from the bag.
*
* @author Brett Wooldridge
*
* @param <T> the templated type to store in the bag
*/
public class ConcurrentBag<T extends com.zaxxer.hikari.util.ConcurrentBag.IBagManagable>
{
public static final int STATE_NOT_IN_USE = 0;
public static final int STATE_IN_USE = 1;
private static final int STATE_REMOVED = -1;
private static final int STATE_RESERVED = -2;
/**
* This interface must be implemented by classes wishing to be managed by
* ConcurrentBag. All implementations must be atomic with respect to state.
* The suggested implementation is via AtomicInteger using the methods
* <code>get()</code> and <code>compareAndSet()</code>.
*/
public interface IBagManagable
{
int getState();
boolean compareAndSetState(int expectedState, int newState);
}
/**
* This interface is implemented by a listener to the bag. The listener
* will be informed of when the bag has become empty. The usual course
* of action by the listener in this case is to attempt to add an item
* to the bag.
*/
public interface IBagStateListener
{
void addBagItem();
}
private ThreadLocal<FastList<WeakReference<T>>> threadList;
private CopyOnWriteArraySet<T> sharedList;
private Synchronizer synchronizer;
private IBagStateListener listener;
/**
* Constructor.
*/
public ConcurrentBag()
{
this.sharedList = new CopyOnWriteArraySet<T>();
this.synchronizer = new Synchronizer();
this.threadList = new ThreadLocal<FastList<WeakReference<T>>>();
}
/**
* The method will borrow an IBagManagable from the bag, blocking for the
* specified timeout if none are available.
*
* @param timeout how long to wait before giving up, in units of unit
* @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter
* @return a borrowed instance from the bag or null if a timeout occurs
* @throws InterruptedException if interrupted while waiting
*/
public T borrow(long timeout, TimeUnit timeUnit) throws InterruptedException
{
// Try the thread-local list first
FastList<WeakReference<T>> list = threadList.get();
if (list == null) {
list = new FastList<WeakReference<T>>(WeakReference.class);
threadList.set(list);
}
else {
for (int i = list.size() - 1; i >= 0; i--) {
final WeakReference<T> reference = list.removeLast();
final T element = reference.get();
if (element != null && element.compareAndSetState(STATE_NOT_IN_USE, STATE_IN_USE)) {
return element;
}
}
}
// Otherwise, scan the shared list ... for maximum of timeout
timeout = timeUnit.toNanos(timeout);
do {
final long startScan = System.nanoTime();
for (T reference : sharedList) {
if (reference.compareAndSetState(STATE_NOT_IN_USE, STATE_IN_USE)) {
return reference;
}
}
if (listener != null) {
listener.addBagItem();
}
synchronizer.tryAcquireSharedNanos(startScan, timeout);
timeout -= (System.nanoTime() - startScan);
}
while (timeout > 0L);
return null;
}
/**
* This method will return a borrowed object to the bag. Objects
* that are borrowed from the bag but never "requited" will result
* in a memory leak.
*
* @param value the value to return to the bag
* @throws NullPointerException if value is null
* @throws IllegalStateException if the requited value was not borrowed from the bag
*/
public void requite(final T value)
{
if (value == null) {
throw new NullPointerException("Cannot return a null value to the bag");
}
if (value.compareAndSetState(STATE_IN_USE, STATE_NOT_IN_USE)) {
FastList<WeakReference<T>> list = threadList.get();
if (list == null) {
list = new FastList<WeakReference<T>>(WeakReference.class);
threadList.set(list);
}
list.add(new WeakReference<T>(value));
synchronizer.releaseShared(System.nanoTime());
}
else {
throw new IllegalStateException("Value was returned to the bag that was not borrowed: " + value);
}
}
/**
* Add a new object to the bag for others to borrow.
*
* @param value an object to add to the bag
*/
public void add(final T value)
{
final long addTime = System.nanoTime();
sharedList.add(value);
synchronizer.releaseShared(addTime);
}
/**
* Remove a value from the bag. This method should only be called
* with objects obtained by {@link #borrow(long, TimeUnit)} or {@link #reserve(IBagManagable)}.
* @param value the value to remove
* @throws IllegalStateException if an attempt is made to remove an object
* from the bag that was not borrowed or reserved first
*/
public void remove(T value)
{
if (value.compareAndSetState(STATE_IN_USE, STATE_REMOVED) || value.compareAndSetState(STATE_RESERVED, STATE_REMOVED)) {
if (!sharedList.remove(value)) {
throw new IllegalStateException("Attempt to remove an object from the bag that does not exist");
}
}
else {
throw new IllegalStateException("Attempt to remove an object from the bag that was not borrowed or reserved");
}
}
/**
* This method provides a "snaphot" in time of the IBagManagable
* items in the bag in the specified state. It does not "lock"
* or reserve items in any way. Call {@link #reserve(IBagManagable)}
* on items in list before performing any action on them.
*
* @param state one of STATE_NOT_IN_USE or STATE_IN_USE
* @return a possibly empty list of objects having the state specified
*/
public List<T> values(int state)
{
if (state == STATE_IN_USE || state == STATE_NOT_IN_USE) {
return sharedList.stream()
.filter(reference -> reference.getState() == state)
.collect(Collectors.toList());
}
return new ArrayList<T>(0);
}
/**
* The method is used to make an item in the bag "unavailable" for
* borrowing. It is primarily used when wanting to operate on items
* returned by the {@link #values(int)} method. Items that are
* reserved can be removed from the bag via {@link #remove(IBagManagable)}
* without the need to unreserve them. Items that are not removed
* from the bag can be make available for borrowing again by calling
* the {@link #unreserve(IBagManagable)} method.
*
* @param value the item to reserve
* @return true if the item was able to be reserved, false otherwise
*/
public boolean reserve(T value)
{
return value.compareAndSetState(STATE_NOT_IN_USE, STATE_RESERVED);
}
/**
* This method is used to make an item reserved via {@link #reserve(IBagManagable)}
* available again for borrowing.
*
* @param value the item to unreserve
*/
public void unreserve(T value)
{
final long checkInTime = System.nanoTime();
if (!value.compareAndSetState(STATE_RESERVED, STATE_NOT_IN_USE)) {
throw new IllegalStateException("Attempt to relinquish an object to the bag that was not reserved");
}
synchronizer.releaseShared(checkInTime);
}
/**
* Add a listener to the bag. There can only be one. If this method is
* called a second time, the original listener will be evicted.
*
* @param listener a listener to the bag
*/
public void addBagStateListener(IBagStateListener listener)
{
this.listener = listener;
}
/**
* Get the number of threads pending (waiting) for an item from the
* bag to become available.
*
* @return the number of threads waiting for items from the bag
*/
public int getPendingQueue()
{
return synchronizer.getQueueLength();
}
public long getCount(int state)
{
return sharedList.stream().filter(reference -> reference.getState() == state).count();
}
/**
* Get the total number of items in the bag.
*
* @return the number of items in the bag
*/
public int size()
{
return sharedList.size();
}
/**
* Our private synchronizer that handles notify/wait type semantics.
*/
private static class Synchronizer extends AbstractQueuedLongSynchronizer
{
private static final long serialVersionUID = 104753538004341218L;
@Override
protected long tryAcquireShared(long startScanTime)
{
return getState() >= startScanTime && !hasQueuedPredecessors() ? 1L : -1L;
}
/** {@inheritDoc} */
@Override
protected boolean tryReleaseShared(long updateTime)
{
setState(updateTime);
return true;
}
}
}

@ -0,0 +1,111 @@
/*
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.util;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import javax.sql.DataSource;
public final class DriverDataSource implements DataSource
{
private final String jdbcUrl;
private final Properties driverProperties;
private PrintWriter logWriter;
public DriverDataSource(String jdbcUrl, Properties properties, String username, String password)
{
try {
this.jdbcUrl = jdbcUrl;
this.driverProperties = new Properties(properties);
if (username != null) {
driverProperties.put("user", driverProperties.getProperty("user", username));
}
if (password != null) {
driverProperties.put("password", driverProperties.getProperty("password", password));
}
if (DriverManager.getDriver(jdbcUrl) == null) {
throw new IllegalArgumentException("DriverManager was unable to load driver for URL " + jdbcUrl);
}
}
catch (SQLException e) {
throw new RuntimeException("Unable to get driver for JDBC URL " + jdbcUrl);
}
}
@Override
public Connection getConnection() throws SQLException
{
return DriverManager.getConnection(jdbcUrl, driverProperties);
}
@Override
public Connection getConnection(String username, String password) throws SQLException
{
return getConnection();
}
@Override
public PrintWriter getLogWriter() throws SQLException
{
return logWriter;
}
@Override
public void setLogWriter(PrintWriter logWriter) throws SQLException
{
this.logWriter = logWriter;
}
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
DriverManager.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException
{
return DriverManager.getLoginTimeout();
}
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException
{
throw new SQLFeatureNotSupportedException();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
throw new SQLFeatureNotSupportedException();
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
}
public void shutdown()
{
}
}

@ -0,0 +1,141 @@
/*
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.util;
import java.lang.reflect.Array;
/**
* Fast list without range checking.
*
* @author Brett Wooldridge
*/
public final class FastList<T>
{
private T[] elementData;
private int size;
/**
* Construct a FastList with a default size of 32.
* @param clazz the Class stored in the collection
*/
@SuppressWarnings("unchecked")
public FastList(Class<?> clazz)
{
this.elementData = (T[]) Array.newInstance(clazz, 32);
}
/**
* Construct a FastList with a specfied size.
* @param clazz the Class stored in the collection
* @param capacity the initial size of the FastList
*/
@SuppressWarnings("unchecked")
public FastList(Class<?> clazz, int capacity)
{
this.elementData = (T[]) Array.newInstance(clazz, capacity);
}
/**
* Add an element to the tail of the FastList.
*
* @param element the element to add
*/
public void add(T element)
{
try {
elementData[size++] = element;
}
catch (ArrayIndexOutOfBoundsException e) {
// overflow-conscious code
final int oldCapacity = elementData.length;
final int newCapacity = oldCapacity << 1;
@SuppressWarnings("unchecked")
final T[] newElementData = (T[]) Array.newInstance(element.getClass(), newCapacity);
System.arraycopy(elementData, 0, newElementData, 0, oldCapacity);
newElementData[size - 1] = element;
elementData = newElementData;
}
}
/**
* Get the element at the specified index.
*
* @param index the index of the element to get
* @return the element, or ArrayIndexOutOfBounds is thrown if the index is invalid
*/
public T get(int index)
{
return elementData[index];
}
/**
* This remove method is most efficient when the element being removed
* is the last element. Equality is identity based, not equals() based.
* Only the first matching element is removed.
*
* @return the last element of the list
*/
public T removeLast()
{
T element = elementData[--size];
elementData[size] = null;
return element;
}
/**
* This remove method is most efficient when the element being removed
* is the last element. Equality is identity based, not equals() based.
* Only the first matching element is removed.
*
* @param element the element to remove
*/
public void remove(T element)
{
for (int index = size - 1; index >= 0; index--) {
if (element == elementData[index]) {
final int numMoved = size - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
}
elementData[--size] = null;
break;
}
}
}
/**
* Clear the FastList.
*/
public void clear()
{
for (int i = 0; i < size; i++) {
elementData[i] = null;
}
size = 0;
}
/**
* Get the current number of elements in the FastList.
*
* @return the number of current elements
*/
public int size()
{
return size;
}
}

@ -0,0 +1,109 @@
package com.zaxxer.hikari.util;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public final class PoolUtilities
{
public static void quietlyCloseConnection(Connection connection)
{
if (connection != null) {
try {
connection.close();
}
catch (SQLException e) {
return;
}
}
}
/**
* Get the elapsed time in millisecond between the specified start time and now.
*
* @param start the start time
* @return the elapsed milliseconds
*/
public static long elapsedTimeMs(long start)
{
return System.currentTimeMillis() - start;
}
/**
* Execute the user-specified init SQL.
*
* @param connection the connection to initialize
* @param sql the SQL to execute
* @throws SQLException throws if the init SQL execution fails
*/
public static void executeSqlAutoCommit(Connection connection, String sql) throws SQLException
{
if (sql != null) {
connection.setAutoCommit(true);
try (Statement statement = connection.createStatement()) {
statement.execute(sql);
}
}
}
public static void quietlySleep(long millis)
{
try {
Thread.sleep(millis);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
public static <T> T createInstance(String className, Class<T> clazz, Object... args)
{
if (className == null) {
return null;
}
try {
Class<?> loaded = PoolUtilities.class.getClassLoader().loadClass(className);
Class<?>[] argClasses = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
argClasses[i] = args[i].getClass();
}
if (args.length > 0) {
Constructor<?> constructor = loaded.getConstructor(argClasses);
return (T) constructor.newInstance(args);
}
return (T) loaded.newInstance();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public static ThreadPoolExecutor createThreadPoolExecutor(final int queueSize, final String threadName)
{
ThreadFactory threadFactory = new ThreadFactory() {
public Thread newThread(Runnable r)
{
Thread t = new Thread(r, threadName);
t.setDaemon(true);
return t;
}
};
int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 2, TimeUnit.SECONDS, queue, threadFactory,
new ThreadPoolExecutor.DiscardPolicy());
executor.allowCoreThreadTimeOut(true);
return executor;
}
}

@ -0,0 +1,148 @@
/*
* 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.util;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
/**
* A class that reflectively sets bean properties on a target object.
*
* @author Brett Wooldridge
*/
public final class PropertyBeanSetter
{
private static final Logger LOGGER = LoggerFactory.getLogger(PropertyBeanSetter.class);
public static void setTargetFromProperties(Object target, Properties properties)
{
if (target == null || properties == null) {
return;
}
for (Entry<Object, Object> propEntry : properties.entrySet()) {
String propName = propEntry.getKey().toString();
Object propValue = propEntry.getValue();
if (target instanceof HikariConfig && propName.startsWith("dataSource.")) {
HikariConfig config = (HikariConfig) target;
config.addDataSourceProperty(propName.substring("dataSource.".length()), propValue);
}
else {
setProperty(target, propName, propValue);
}
}
}
/**
* Get the bean-style property names for the specified object.
*
* @param targetClass the target object
* @return a set of property names
*/
public static Set<String> getPropertyNames(Class<?> targetClass)
{
HashSet<String> set = new HashSet<String>();
try {
BeanInfo info = Introspector.getBeanInfo(targetClass);
for (PropertyDescriptor descr : info.getPropertyDescriptors()) {
if (!"class".equals(descr.getName())) {
set.add(descr.getName());
}
}
return set;
}
catch (IntrospectionException e) {
throw new RuntimeException(e);
}
}
public static Object getProperty(String propName, Object target)
{
try {
String capitalized = "get" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
Method method = target.getClass().getMethod(capitalized);
return method.invoke(target);
}
catch (Exception e) {
try {
String capitalized = "is" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
Method method = target.getClass().getMethod(capitalized);
return method.invoke(target);
}
catch (Exception e2) {
return null;
}
}
}
private static void setProperty(Object target, String propName, Object propValue)
{
String capitalized = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);
PropertyDescriptor propertyDescriptor;
try {
propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
}
catch (IntrospectionException e) {
capitalized = "set" + propName.toUpperCase();
try {
propertyDescriptor = new PropertyDescriptor(propName, target.getClass(), null, capitalized);
}
catch (IntrospectionException e1) {
LOGGER.error("Property {} is does not exist on target class {}", propName, target.getClass());
throw new RuntimeException(e);
}
}
try {
Method writeMethod = propertyDescriptor.getWriteMethod();
Class<?> paramClass = writeMethod.getParameterTypes()[0];
if (paramClass == int.class) {
writeMethod.invoke(target, Integer.parseInt(propValue.toString()));
}
else if (paramClass == long.class) {
writeMethod.invoke(target, Long.parseLong(propValue.toString()));
}
else if (paramClass == boolean.class) {
writeMethod.invoke(target, Boolean.parseBoolean(propValue.toString()));
}
else if (paramClass == String.class) {
writeMethod.invoke(target, propValue.toString());
}
else {
writeMethod.invoke(target, propValue);
}
}
catch (Exception e) {
LOGGER.error("Exception setting property {} on target class {}", propName, target.getClass(), e);
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,210 @@
# This is a manually maintained codex of JDBC drivers and the
# classes within that need to be instrumented.
#
# ------ HSQLDB v2.3.1 ----------------------------------------------------------
#
org.hsqldb.jdbc.JDBCDataSource=hsqldb
hsqldb.baseConnection=org.hsqldb.jdbc.JDBCConnection
hsqldb.baseStatement=org.hsqldb.jdbc.JDBCStatement
hsqldb.basePreparedStatement=org.hsqldb.jdbc.JDBCPreparedStatement
hsqldb.subCallableStatement=org.hsqldb.jdbc.JDBCCallableStatement
hsqldb.baseResultSet=org.hsqldb.jdbc.JDBCResultSet
hsqldb.subResultSet=org.hsqldb.jdbc.JDBCResultSet$JDBCResultSetBasic
# ------ Derby v10.10.1.1 -------------------------------------------------------
#
org.apache.derby.jdbc.ClientDataSource40=derby
derby.baseConnection=org.apache.derby.client.am.Connection
derby.subConnection=org.apache.derby.client.net.NetConnection40
derby.baseStatement=org.apache.derby.client.am.Statement
derby.subPreparedStatement=org.apache.derby.client.am.PreparedStatement
derby.subPreparedStatement=org.apache.derby.client.am.PreparedStatement40
#derby.subPreparedStatement=org.apache.derby.client.am.PreparedStatement42
derby.subCallableStatement=org.apache.derby.client.am.CallableStatement
derby.subCallableStatement=org.apache.derby.client.am.CallableStatement40
#derby.subCallableStatement=org.apache.derby.client.am.CallableStatement42
derby.baseResultSet=org.apache.derby.client.am.ResultSet
derby.subResultSet=org.apache.derby.client.net.NetResultSet40
#derby.subResultSet=org.apache.derby.client.net.NetResultSet42
# ------ Derby Embedded v10.10.1.1 ----------------------------------------------
#
derby-em.baseConnection=org.apache.derby.impl.jdbc.EmbedConnection
derby-em.baseConnection=org.apache.derby.iapi.jdbc.BrokeredConnection
derby-em.subConnection=org.apache.derby.impl.jdbc.EmbedConnection40
derby-em.subConnection=org.apache.derby.iapi.jdbc.BrokeredConnection40
#derby-em.subConnection=org.apache.derby.iapi.jdbc.BrokeredConnection42
derby-em.baseStatement=org.apache.derby.impl.jdbc.EmbedStatement
derby-em.baseStatement=org.apache.derby.iapi.jdbc.BrokeredStatement
derby-em.baseStatement=org.apache.derby.client.am.Statement
derby-em.subStatement=org.apache.derby.impl.jdbc.EmbedStatement40
derby-em.subStatement=org.apache.derby.iapi.jdbc.BrokeredStatement40
derby-em.subPreparedStatement=org.apache.derby.impl.jdbc.EmbedPreparedStatement
derby-em.subPreparedStatement=org.apache.derby.impl.jdbc.EmbedPreparedStatement20
derby-em.subPreparedStatement=org.apache.derby.impl.jdbc.EmbedPreparedStatement30
derby-em.subPreparedStatement=org.apache.derby.impl.jdbc.EmbedPreparedStatement40
#derby-em.subPreparedStatement=org.apache.derby.impl.jdbc.EmbedPreparedStatement42
derby-em.subPreparedStatement=org.apache.derby.iapi.jdbc.BrokeredPreparedStatement
derby-em.subPreparedStatement=org.apache.derby.iapi.jdbc.BrokeredPreparedStatement40
#derby-em.subPreparedStatement=org.apache.derby.iapi.jdbc.BrokeredPreparedStatement42
derby-em.subPreparedStatement=org.apache.derby.client.am.PreparedStatement
derby-em.subPreparedStatement=org.apache.derby.client.am.PreparedStatement40
#derby-em.subPreparedStatement=org.apache.derby.client.am.PreparedStatement42
derby-em.subCallableStatement=org.apache.derby.impl.jdbc.EmbedCallableStatement
derby-em.subCallableStatement=org.apache.derby.impl.jdbc.EmbedCallableStatement20
derby-em.subCallableStatement=org.apache.derby.impl.jdbc.EmbedCallableStatement30
derby-em.subCallableStatement=org.apache.derby.impl.jdbc.EmbedCallableStatement40
#derby-em.subCallableStatement=org.apache.derby.impl.jdbc.EmbedCallableStatement42
derby-em.subCallableStatement=org.apache.derby.iapi.jdbc.BrokeredCallableStatement
derby-em.subCallableStatement=org.apache.derby.iapi.jdbc.BrokeredCallableStatement40
#derby-em.subCallableStatement=org.apache.derby.iapi.jdbc.BrokeredCallableStatement42
derby-em.subCallableStatement=org.apache.derby.client.am.CallableStatement
derby-em.subCallableStatement=org.apache.derby.client.am.CallableStatement40
#derby-em.subCallableStatement=org.apache.derby.client.am.CallableStatement42
# ------ PostgreSQL v9.2-1003.jdbc4 ----------------------------------------------
#
org.postgresql.ds.PGSimpleDataSource=pgsql
pgsql.baseConnection=org.postgresql.jdbc2.AbstractJdbc2Connection
pgsql.subConnection=org.postgresql.jdbc3.AbstractJdbc3Connection
pgsql.subConnection=org.postgresql.jdbc3g.AbstractJdbc3gConnection
pgsql.subConnection=org.postgresql.jdbc4.AbstractJdbc4Connection
pgsql.subConnection=org.postgresql.jdbc4.Jdbc4Connection
pgsql.basePreparedStatement=org.postgresql.jdbc2.AbstractJdbc2Statement
pgsql.subPreparedStatement=org.postgresql.jdbc3.AbstractJdbc3Statement
pgsql.subPreparedStatement=org.postgresql.jdbc3g.AbstractJdbc3gStatement
pgsql.subPreparedStatement=org.postgresql.jdbc4.AbstractJdbc4Statement
pgsql.subPreparedStatement=org.postgresql.jdbc4.Jdbc4Statement
pgsql.subPreparedStatement=org.postgresql.jdbc4.Jdbc4PreparedStatement
pgsql.subCallableStatement=org.postgresql.jdbc4.Jdbc4CallableStatement
pgsql.baseResultSet=org.postgresql.jdbc2.AbstractJdbc2ResultSet
pgsql.subResultSet=org.postgresql.jdbc3.AbstractJdbc3ResultSet
pgsql.subResultSet=org.postgresql.jdbc3g.AbstractJdbc3gResultSet
pgsql.subResultSet=org.postgresql.jdbc4.AbstractJdbc4ResultSet
pgsql.subResultSet=org.postgresql.jdbc4.Jdbc4ResultSet
# ------ MySQL Connector/J v5.1.26 ----------------------------------------------
#
com.mysql.jdbc.jdbc2.optional.MysqlDataSource=mysql
mysql.baseConnection=com.mysql.jdbc.ConnectionImpl
mysql.baseConnection=com.mysql.jdbc.ReplicationConnection
mysql.baseConnection=com.mysql.jdbc.LoadBalancedMySQLConnection
mysql.subConnection=com.mysql.jdbc.JDBC4Connection
mysql.subConnection=com.mysql.jdbc.JDBC4LoadBalancedMySQLConnection
mysql.baseStatement=com.mysql.jdbc.StatementImpl
mysql.subPreparedStatement=com.mysql.jdbc.PreparedStatement
mysql.subPreparedStatement=com.mysql.jdbc.ServerPreparedStatement
mysql.subPreparedStatement=com.mysql.jdbc.JDBC4ServerPreparedStatement
mysql.subCallableStatement=com.mysql.jdbc.CallableStatement
mysql.subCallableStatement=com.mysql.jdbc.JDBC4CallableStatement
mysql.baseResultSet=com.mysql.jdbc.ResultSetImpl
mysql.subResultSet=com.mysql.jdbc.JDBC4ResultSet
mysql.subResultSet=com.mysql.jdbc.UpdatableResultSet
mysql.subResultSet=com.mysql.jdbc.JDBC4UpdatableResultSet
# ------ MariaDB JDBC v1.1.5 --------------------------------------------------
#
org.mariadb.jdbc.MySQLDataSource=maria
maria.baseConnection=org.mariadb.jdbc.MySQLConnection
maria.baseStatement=org.mariadb.jdbc.MySQLStatement
maria.subPreparedStatement=org.mariadb.jdbc.MySQLPreparedStatement
maria.baseCallableStatement=org.mariadb.jdbc.MySQLCallableStatement
maria.baseResultSet=org.mariadb.jdbc.MySQLResultSet
# ------ JTDS v1.3.1 ----------------------------------------------------------
#
net.sourceforge.jtds.jdbcx.JtdsDataSource=jtds
jtds.baseConnection=net.sourceforge.jtds.jdbc.JtdsConnection
jtds.baseConnection=net.sourceforge.jtds.jdbcx.proxy.ConnectionProxy
jtds.baseStatement=net.sourceforge.jtds.jdbc.JtdsStatement
jtds.baseStatement=net.sourceforge.jtds.jdbcx.proxy.StatementProxy
jtds.subPreparedStatement=net.sourceforge.jtds.jdbc.JtdsPreparedStatement
jtds.subPreparedStatement=net.sourceforge.jtds.jdbcx.proxy.PreparedStatementProxy
jtds.subCallableStatement=net.sourceforge.jtds.jdbc.JtdsCallableStatement
jtds.subCallableStatement=net.sourceforge.jtds.jdbcx.proxy.CallableStatementProxy
jtds.baseResultSet=net.sourceforge.jtds.jdbc.JtdsResultSet
jtds.subResultSet=net.sourceforge.jtds.jdbc.CachedResultSet
jtds.subResultSet=net.sourceforge.jtds.jdbc.MSCursorResultSet
# ------ Oracle v12.1.0.1 -----------------------------------------------------
#
oracle.jdbc.pool.OracleDataSource=oracle
oracle.baseConnection=oracle.jdbc.driver.OracleConnection
oracle.subConnection=oracle.jdbc.driver.PhysicalConnection
oracle.subConnection=oracle.jdbc.driver.T4CConnection
oracle.baseStatement=oracle.jdbc.driver.OracleStatement
oracle.subPreparedStatement=oracle.jdbc.driver.OraclePreparedStatement
oracle.subCallableStatement=oracle.jdbc.driver.OracleCallableStatement
oracle.baseResultSet=oracle.jdbc.driver.OracleResultSet
oracle.subResultSet=oracle.jdbc.driver.ArrayDataResultSet
oracle.subResultSet=oracle.jdbc.driver.GeneratedScrollableResultSet
oracle.subResultSet=oracle.jdbc.driver.InsensitiveScrollableResultSet
oracle.subResultSet=oracle.jdbc.driver.ForwardOnlyResultSet
oracle.subResultSet=oracle.jdbc.driver.ArrayLocatorResultSet
oracle.subResultSet=oracle.jdbc.driver.SensitiveScrollableResultSet
oracle.subResultSet=oracle.jdbc.driver.OracleReturnResultSet
oracle.subResultSet=oracle.jdbc.driver.GeneratedUpdatableResultSet
oracle.subResultSet=oracle.jdbc.driver.UpdatableResultSet
# ------ HikariCP Testing v0.0.0 ----------------------------------------------
#
com.zaxxer.hikari.mocks.StubDataSource=hikari
hikari.baseConnection=com.zaxxer.hikari.mocks.StubBaseConnection
hikari.subConnection=com.zaxxer.hikari.mocks.StubConnection
hikari.baseStatement=com.zaxxer.hikari.mocks.StubStatement
hikari.subPreparedStatement=com.zaxxer.hikari.mocks.StubPreparedStatement
hikari.baseResultSet=com.zaxxer.hikari.mocks.StubResultSet
com.zaxxer.hikari.performance.StubDataSource=hikari-perf
hikari-perf.baseConnection=com.zaxxer.hikari.performance.StubConnection
hikari-perf.baseStatement=com.zaxxer.hikari.performance.StubStatement
hikari-perf.subPreparedStatement=com.zaxxer.hikari.performance.StubPreparedStatement
hikari-perf.baseResultSet=com.zaxxer.hikari.performance.StubResultSet

@ -0,0 +1,103 @@
package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
public class ConnectionStateTest
{
@Test
public void testAutoCommit() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setAutoCommit(true);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try
{
Connection connection = ds.getConnection();
connection.setAutoCommit(false);
connection.close();
Connection connection2 = ds.getConnection();
Assert.assertSame(connection, connection2);
Assert.assertTrue(connection2.getAutoCommit());
connection2.close();
}
finally
{
ds.shutdown();
}
}
@Test
public void testTransactionIsolation() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try
{
Connection connection = ds.getConnection();
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
connection.close();
Connection connection2 = ds.getConnection();
Assert.assertSame(connection, connection2);
Assert.assertEquals(Connection.TRANSACTION_READ_COMMITTED, connection2.getTransactionIsolation());
connection2.close();
}
finally
{
ds.shutdown();
}
}
@Test
public void testIsolation() throws Exception
{
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setTransactionIsolation("TRANSACTION_REPEATABLE_READ");
config.validate();
int transactionIsolation = config.getTransactionIsolation();
Assert.assertSame(Connection.TRANSACTION_REPEATABLE_READ, transactionIsolation);
}
@Test
public void testCatalog() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setCatalog("test");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try
{
Connection connection = ds.getConnection();
connection.setCatalog("other");
connection.close();
Connection connection2 = ds.getConnection();
Assert.assertSame(connection, connection2);
Assert.assertEquals("test", connection2.getCatalog());
connection2.close();
}
finally
{
ds.shutdown();
}
}
}

@ -0,0 +1,118 @@
package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ExceptionTest
{
private HikariDataSource ds;
@Before
public void setup()
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds = new HikariDataSource(config);
}
@After
public void teardown()
{
ds.shutdown();
}
@Test
public void testException1() throws SQLException
{
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
Assert.assertNotNull(statement);
ResultSet resultSet = statement.executeQuery();
Assert.assertNotNull(resultSet);
try
{
statement.getMaxFieldSize();
Assert.fail();
}
catch (Exception e)
{
Assert.assertSame(SQLException.class, e.getClass());
}
connection.close();
Assert.assertSame("Totals connections not as expected", 0, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
}
@Test
public void testUseAfterStatementClose() throws SQLException
{
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
try
{
PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
statement.close();
statement.getMoreResults();
Assert.fail();
}
catch (SQLException e)
{
Assert.assertSame("Connection is closed", e.getMessage());
}
}
@Test
public void testUseAfterClose() throws SQLException
{
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
connection.close();
try
{
connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
Assert.fail();
}
catch (SQLException e)
{
Assert.assertSame("Connection is closed", e.getMessage());
}
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
}

@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.util.DriverDataSource;
public class JdbcDriverTest
{
private HikariDataSource ds;
@After
public void teardown()
{
ds.shutdown();
}
@Test
public void driverTest1() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setConnectionTestQuery("VALUES 1");
config.setDriverClassName("com.zaxxer.hikari.mocks.StubDriver");
config.setJdbcUrl("jdbc:stub");
config.addDataSourceProperty("user", "bart");
config.addDataSourceProperty("password", "simpson");
ds = new HikariDataSource(config);
Assert.assertTrue(ds.isWrapperFor(DriverDataSource.class));
DriverDataSource unwrap = ds.unwrap(DriverDataSource.class);
Assert.assertNotNull(unwrap);
Connection connection = ds.getConnection();
connection.close();
}
}

@ -0,0 +1,47 @@
package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
public class RampUpDown
{
@Test
public void rampUpDownTest() throws SQLException, InterruptedException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(60);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "250");
HikariDataSource ds = new HikariDataSource(config);
ds.setIdleTimeout(1000);
Assert.assertSame("Totals connections not as expected", 5, TestElf.getPool(ds).getTotalConnections());
Connection[] connections = new Connection[ds.getMaximumPoolSize()];
for (int i = 0; i < connections.length; i++)
{
connections[i] = ds.getConnection();
}
Assert.assertSame("Totals connections not as expected", 60, TestElf.getPool(ds).getTotalConnections());
for (Connection connection : connections)
{
connection.close();
}
Thread.sleep(2500);
Assert.assertSame("Totals connections not as expected", 5, TestElf.getPool(ds).getTotalConnections());
ds.shutdown();
}
}

@ -0,0 +1,193 @@
/*
* Copyright (C) 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.zaxxer.hikari.mocks.StubConnection;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.util.PoolUtilities;
/**
* @author Brett Wooldridge
*/
public class ShutdownTest
{
@Before
public void beforeTest()
{
StubConnection.count.set(0);
}
@After
public void afterTest()
{
StubConnection.slowCreate = false;
}
@Test
public void testShutdown1() throws SQLException
{
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true;
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(10);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
final HikariDataSource ds = new HikariDataSource(config);
HikariPool pool = TestElf.getPool(ds);
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++)
{
threads[i] = new Thread() {
public void run() {
try
{
if (ds.getConnection() != null)
{
PoolUtilities.quietlySleep(TimeUnit.SECONDS.toMillis(1));
}
}
catch (SQLException e)
{
}
}
};
threads[i].setDaemon(true);
threads[i].start();
}
PoolUtilities.quietlySleep(300);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
ds.shutdown();
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
}
@Test
public void testShutdown2() throws SQLException
{
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true;
HikariConfig config = new HikariConfig();
config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
config.setInitializationFailFast(false);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
HikariPool pool = TestElf.getPool(ds);
PoolUtilities.quietlySleep(300);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
ds.shutdown();
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
}
@Test
public void testShutdown3() throws SQLException
{
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true;
HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
HikariPool pool = TestElf.getPool(ds);
PoolUtilities.quietlySleep(300);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
ds.shutdown();
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
}
@Test
public void testShutdown4() throws SQLException
{
StubConnection.slowCreate = true;
HikariConfig config = new HikariConfig();
config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
config.setInitializationFailFast(false);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
PoolUtilities.quietlySleep(300);
ds.shutdown();
long start = System.currentTimeMillis();
while (PoolUtilities.elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5) && threadCount() > 0)
{
PoolUtilities.quietlySleep(250);
}
Assert.assertSame("Thread was leaked", 0, threadCount());
}
private int threadCount()
{
Thread[] threads = new Thread[Thread.activeCount() * 2];
Thread.enumerate(threads);
int count = 0;
for (Thread thread : threads)
{
count += (thread != null && thread.getName().startsWith("Hikari")) ? 1 : 0;
}
return count;
}
}

@ -0,0 +1,98 @@
package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class StatementTest
{
private HikariDataSource ds;
@Before
public void setup()
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(2);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds = new HikariDataSource(config);
}
@After
public void teardown()
{
ds.shutdown();
}
@Test
public void testStatementClose() throws SQLException
{
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
Statement statement = connection.createStatement();
Assert.assertNotNull(statement);
connection.close();
Assert.assertTrue(statement.isClosed());
}
@Test
public void testAutoStatementClose() throws SQLException
{
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Statement statement1 = connection.createStatement();
Assert.assertNotNull(statement1);
Statement statement2 = connection.createStatement();
Assert.assertNotNull(statement2);
connection.close();
Assert.assertTrue(statement1.isClosed());
Assert.assertTrue(statement2.isClosed());
}
@Test
public void testDoubleStatementClose() throws SQLException
{
Connection connection = ds.getConnection();
Statement statement1 = connection.createStatement();
statement1.close();
statement1.close();
connection.close();
}
@Test
public void testOutOfOrderStatementClose() throws SQLException
{
Connection connection = ds.getConnection();
Statement statement1 = connection.createStatement();
Statement statement2 = connection.createStatement();
statement1.close();
statement2.close();
connection.close();
}
}

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

@ -0,0 +1,325 @@
/*
* 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.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.mocks.StubConnection;
/**
* System property testProxy can be one of:
* "com.zaxxer.hikari.JavaProxyFactory"
* "com.zaxxer.hikari.CglibProxyFactory"
* "com.zaxxer.hikari.JavassistProxyFactory"
*
* @author Brett Wooldridge
*/
public class TestConnections
{
@Test
public void testCreate() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try
{
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
PreparedStatement statement = connection.prepareStatement("SELECT * FROM device WHERE device_id=?");
Assert.assertNotNull(statement);
statement.setInt(1, 0);
ResultSet resultSet = statement.executeQuery();
Assert.assertNotNull(resultSet);
Assert.assertFalse(resultSet.next());
resultSet.close();
statement.close();
connection.close();
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally
{
ds.shutdown();
}
}
@Test
public void testMaxLifetime() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100");
HikariDataSource ds = new HikariDataSource(config);
try
{
System.clearProperty("com.zaxxer.hikari.housekeeping.periodMs");
ds.setMaxLifetime(700);
Assert.assertSame("Total connections not as expected", 0, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Second total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
connection.close();
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection2 = ds.getConnection();
Assert.assertSame("Expected the same connection", connection, connection2);
Assert.assertSame("Second total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
connection2.close();
Thread.sleep(2000);
connection2 = ds.getConnection();
Assert.assertNotSame("Expected a different connection", connection, connection2);
connection2.close();
Assert.assertSame("Post total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Post idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally
{
ds.shutdown();
}
}
@Test
public void testMaxLifetime2() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try
{
ds.setMaxLifetime(700);
Assert.assertSame("Total connections not as expected", 0, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Second total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
connection.close();
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection2 = ds.getConnection();
Assert.assertSame("Expected the same connection", connection, connection2);
Assert.assertSame("Second total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
connection2.close();
Thread.sleep(800);
connection2 = ds.getConnection();
Assert.assertNotSame("Expected a different connection", connection, connection2);
connection2.close();
Assert.assertSame("Post total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Post idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally
{
ds.shutdown();
}
}
@Test
public void testDoubleClose() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try
{
Connection connection = ds.getConnection();
connection.close();
connection.close();
}
finally
{
ds.shutdown();
}
}
@Test
public void testBackfill() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setConnectionTimeout(500);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try
{
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
// This will take the pool down to zero
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
Assert.assertNotNull(statement);
ResultSet resultSet = statement.executeQuery();
Assert.assertNotNull(resultSet);
try
{
statement.getMaxFieldSize();
Assert.fail();
}
catch (Exception e)
{
Assert.assertSame(SQLException.class, e.getClass());
}
// The connection will be ejected from the pool here
connection.close();
Assert.assertSame("Totals connections not as expected", 0, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
// This will cause a backfill
connection = ds.getConnection();
connection.close();
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally
{
ds.shutdown();
}
}
@Test
public void testMaximumPoolLimit() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setConnectionTimeout(TimeUnit.MINUTES.toMillis(1));
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
StubConnection.count.set(0);
final HikariDataSource ds = new HikariDataSource(config);
try
{
Thread[] threads = new Thread[20];
for (int i = 0; i < threads.length; i++)
{
threads[i] = new Thread(new Runnable() {
public void run()
{
try
{
Connection connection = ds.getConnection();
Thread.sleep(1000);
connection.close();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
for (int i = 0; i < threads.length; i++)
{
threads[i].start();
}
for (int i = 0; i < threads.length; i++)
{
threads[i].join();
}
Assert.assertEquals(4, StubConnection.count.get());
}
finally
{
ds.shutdown();
}
}
}

@ -0,0 +1,48 @@
/*
* 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.lang.reflect.Field;
import com.zaxxer.hikari.pool.HikariPool;
/**
* Utility methods for testing.
*
* @author Brett Wooldridge
*/
public final class TestElf
{
private TestElf()
{
// default constructor
}
public static HikariPool getPool(HikariDataSource ds)
{
try
{
Field field = ds.getClass().getDeclaredField("pool");
field.setAccessible(true);
return (HikariPool) field.get(ds);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,77 @@
package com.zaxxer.hikari;
import java.sql.Statement;
import java.util.ArrayList;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.mocks.StubStatement;
import com.zaxxer.hikari.util.FastList;
public class TestFastList
{
@Test
public void testAddRemove()
{
ArrayList<Statement> verifyList = new ArrayList<Statement>();
FastList<Statement> list = new FastList<Statement>(Statement.class);
for (int i = 0; i < 32; i++)
{
StubStatement statement = new StubStatement(null);
list.add(statement);
verifyList.add(statement);
}
for (int i = 0; i < 32; i++)
{
Assert.assertNotNull("Element " + i + " was null but should be " + verifyList.get(i), list.get(0));
int size = list.size();
list.remove(verifyList.get(i));
Assert.assertSame(size - 1, list.size());
}
}
@Test
public void testAddRemoveTail()
{
ArrayList<Statement> verifyList = new ArrayList<Statement>();
FastList<Statement> list = new FastList<Statement>(Statement.class);
for (int i = 0; i < 32; i++)
{
StubStatement statement = new StubStatement(null);
list.add(statement);
verifyList.add(statement);
}
for (int i = 31; i >= 0; i--)
{
Assert.assertNotNull("Element " + i, list.get(i));
int size = list.size();
list.remove(verifyList.get(i));
Assert.assertSame(size - 1, list.size());
}
}
@Test
public void testOverflow()
{
ArrayList<Statement> verifyList = new ArrayList<Statement>();
FastList<Statement> list = new FastList<Statement>(Statement.class);
for (int i = 0; i < 100; i++)
{
StubStatement statement = new StubStatement(null);
list.add(statement);
verifyList.add(statement);
}
for (int i = 0; i < 100; i++)
{
Assert.assertNotNull("Element " + i, list.get(i));
Assert.assertSame(verifyList.get(i), list.get(i));
}
}
}

@ -0,0 +1,38 @@
/*
* 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.SQLException;
import org.junit.Test;
public class TestMBean
{
@Test
public void testMBeanRegistration() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setRegisterMbeans(true);
config.setConnectionTimeout(2800);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
ds.close();
}
}

@ -0,0 +1,73 @@
package com.zaxxer.hikari;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintWriter;
import java.util.Set;
import javax.sql.DataSource;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.util.PropertyBeanSetter;
public class TestPropertySetter
{
@Test
public void testProperty1()
{
File file = new File("src/test/resources/propfile1.properties");
HikariConfig config = new HikariConfig(file.getPath());
config.validate();
Assert.assertEquals(5, config.getMinimumIdle());
Assert.assertEquals("SELECT 1", config.getConnectionTestQuery());
}
@Test
public void testProperty2() throws Exception
{
File file = new File("src/test/resources/propfile2.properties");
HikariConfig config = new HikariConfig(file.getPath());
config.validate();
Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
DataSource dataSource = (DataSource) clazz.newInstance();
PropertyBeanSetter.setTargetFromProperties(dataSource, config.getDataSourceProperties());
}
@Test
public void testObjectProperty() throws Exception
{
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
PrintWriter writer = new PrintWriter(new ByteArrayOutputStream());
config.addDataSourceProperty("logWriter", writer);
Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
DataSource dataSource = (DataSource) clazz.newInstance();
PropertyBeanSetter.setTargetFromProperties(dataSource, config.getDataSourceProperties());
Assert.assertSame(PrintWriter.class, dataSource.getLogWriter().getClass());
}
@Test
public void testPropertyUpperCase() throws Exception
{
File file = new File("src/test/resources/propfile3.properties");
HikariConfig config = new HikariConfig(file.getPath());
config.validate();
Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
DataSource dataSource = (DataSource) clazz.newInstance();
PropertyBeanSetter.setTargetFromProperties(dataSource, config.getDataSourceProperties());
}
@Test
public void testGetPropertyNames() throws Exception
{
Set<String> propertyNames = PropertyBeanSetter.getPropertyNames(HikariConfig.class);
Assert.assertTrue(propertyNames.contains("dataSourceClassName"));
}
}

@ -0,0 +1,59 @@
/*
* 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;
import org.junit.Assert;
import org.junit.Test;
import com.zaxxer.hikari.mocks.StubConnection;
/**
* @author Brett Wooldridge
*/
public class UnwrapTest
{
@Test
public void testUnwrapConnection() throws SQLException
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try
{
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
StubConnection unwrapped = connection.unwrap(StubConnection.class);
Assert.assertTrue("unwrapped connection is not instance of StubConnection: " + unwrapped, (unwrapped != null && unwrapped instanceof StubConnection));
}
finally
{
ds.shutdown();
}
}
}

@ -0,0 +1,162 @@
/*
* 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.mocks;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.PrintWriter;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
*
* @author Brett Wooldridge
*/
public class MockDataSource implements DataSource
{
@Override
public Connection getConnection() throws SQLException
{
return createMockConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException
{
return getConnection();
}
@Override
public PrintWriter getLogWriter() throws SQLException
{
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException
{
}
@Override
public void setLoginTimeout(int seconds) throws SQLException
{
}
@Override
public int getLoginTimeout() throws SQLException
{
return 0;
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException
{
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException
{
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
{
return false;
}
public static Connection createMockConnection() throws SQLException {
// Setup mock connection
final Connection mockConnection = mock(Connection.class);
// Autocommit is always true by default
when(mockConnection.getAutoCommit()).thenReturn(true);
// Handle Connection.createStatement()
Statement statement = mock(Statement.class);
when(mockConnection.createStatement()).thenReturn(statement);
when(mockConnection.createStatement(anyInt(), anyInt())).thenReturn(statement);
when(mockConnection.createStatement(anyInt(), anyInt(), anyInt())).thenReturn(statement);
when(mockConnection.isValid(anyInt())).thenReturn(true);
// Handle Connection.prepareStatement()
PreparedStatement mockPreparedStatement = mock(PreparedStatement.class);
when(mockConnection.prepareStatement(anyString())).thenReturn(mockPreparedStatement);
when(mockConnection.prepareStatement(anyString(), anyInt())).thenReturn(mockPreparedStatement);
when(mockConnection.prepareStatement(anyString(), (int[]) anyObject())).thenReturn(mockPreparedStatement);
when(mockConnection.prepareStatement(anyString(), (String[]) anyObject())).thenReturn(mockPreparedStatement);
when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt())).thenReturn(mockPreparedStatement);
when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(mockPreparedStatement);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable
{
return null;
}
}).doNothing().when(mockPreparedStatement).setInt(anyInt(), anyInt());
ResultSet mockResultSet = mock(ResultSet.class);
when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet);
when(mockResultSet.getString(anyInt())).thenReturn("aString");
when(mockResultSet.next()).thenReturn(true);
// Handle Connection.prepareCall()
CallableStatement mockCallableStatement = mock(CallableStatement.class);
when(mockConnection.prepareCall(anyString())).thenReturn(mockCallableStatement);
when(mockConnection.prepareCall(anyString(), anyInt(), anyInt())).thenReturn(mockCallableStatement);
when(mockConnection.prepareCall(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(mockCallableStatement);
// Handle Connection.close()
// doAnswer(new Answer<Void>() {
// public Void answer(InvocationOnMock invocation) throws Throwable {
// return null;
// }
// }).doThrow(new SQLException("Connection is already closed")).when(mockConnection).close();
// Handle Connection.commit()
// doAnswer(new Answer<Void>() {
// public Void answer(InvocationOnMock invocation) throws Throwable {
// return null;
// }
// }).doThrow(new SQLException("Transaction already commited")).when(mockConnection).commit();
// Handle Connection.rollback()
// doAnswer(new Answer<Void>() {
// public Void answer(InvocationOnMock invocation) throws Throwable {
// return null;
// }
// }).doThrow(new SQLException("Transaction already rolledback")).when(mockConnection).rollback();
return mockConnection;
}
}

@ -0,0 +1,23 @@
package com.zaxxer.hikari.mocks;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
public abstract class StubBaseConnection implements Connection
{
/** {@inheritDoc} */
@Override
public Statement createStatement() throws SQLException
{
return new StubStatement(this);
}
/** {@inheritDoc} */
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException
{
return new StubPreparedStatement(this);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save