Prepare for Java 6/7 to Java 8 split.
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…
Reference in New Issue