Merge branch 'master' into dev
* master: (40 commits) Fix test breakage, plus some minor cleanup. Remove multi-pool (DataSource.getConnection(username, password) support. Exclude PoolInitializationException from coverage Update README.md Added jacoco to pom Added profile for coveralls Update .travis.yml Change travis-ci configuration for master More improved logging. If the pool is maxed out, return 'true' from addConnection() so we stop trying to add the connection. Fix #258 Improve logging of closed connection reason Recombine into a single Java 7 compatible build. Update README.md Fix #288 allow explicit definition of driverClassName to override DriverManager.getDriver(url) located driver. Mark scope as 'test' Update README.md Update CHANGES [maven-release-plugin] prepare for next development iteration [maven-release-plugin] prepare release HikariCP-2.3.5 Fix #286 fix regression in Dropwizard runtime dependency ... Conflicts: .travis.yml README.md hikaricp-common/pom.xml hikaricp-common/src/main/java/com/zaxxer/hikari/AbstractHikariConfig.java hikaricp-common/src/main/java/com/zaxxer/hikari/HikariConfig.java hikaricp-common/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java hikaricp-java6/src/test/java/com/zaxxer/hikari/TestMetrics.java hikaricp-java7/pom.xml hikaricp-java7/src/main/java/com/zaxxer/hikari/pool/HikariPool.java hikaricp-java7/src/main/java/com/zaxxer/hikari/util/Java7ConcurrentBag.java hikaricp-java7/src/main/resources/META-INF/codex.properties hikaricp-java7/src/test/java/com/zaxxer/hikari/TestMetrics.java hikaricp-java7/src/test/java/com/zaxxer/hikari/osgi/OSGiBundleTest.java hikaricp-java7/src/test/resources/hibernate.properties hikaricp-java7/src/test/resources/propfile1.properties hikaricp-java7/src/test/resources/propfile2.properties hikaricp-java7/src/test/resources/propfile3.properties hikaricp/pom.xml hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java hikaricp/src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java hikaricp/src/main/java/com/zaxxer/hikari/util/Java8ConcurrentBag.java pom.xml src/main/java/com/zaxxer/hikari/HikariConfig.java src/main/java/com/zaxxer/hikari/pool/HikariPool.java src/main/java/com/zaxxer/hikari/proxy/JavassistProxyFactory.java src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java src/test/java/com/zaxxer/hikari/TestConcurrentBag.java src/test/java/com/zaxxer/hikari/TestMetrics.javapull/295/head
commit
37d5cc722e
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Brett Wooldridge
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zaxxer.hikari;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Interface whose implementers can perform one-time customization of a
|
||||
* Connection before it is added to the pool. Note the implemention
|
||||
* of the <code>customize()</code> method must be multithread-safe as
|
||||
* it may be called by multiple threads at one time.
|
||||
*
|
||||
* @author Brett Wooldridge
|
||||
*/
|
||||
public interface IConnectionCustomizer
|
||||
{
|
||||
/**
|
||||
* The Connection object that is passed into this method is the "raw"
|
||||
* Connection instance provided by the JDBC driver, not a wrapped
|
||||
* HikariCP connection.
|
||||
*
|
||||
* @param connection a native JDBC driver Connection instance to customize
|
||||
* @throws SQLException should be thrown if an error condition is encountered during customization
|
||||
*/
|
||||
void customize(Connection connection) throws SQLException;
|
||||
}
|
@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Brett Wooldridge
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zaxxer.hikari;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.health.HealthCheckRegistry;
|
||||
import com.zaxxer.hikari.proxy.JavassistProxyFactory;
|
||||
import com.zaxxer.hikari.util.PropertyBeanSetter;
|
||||
import com.zaxxer.hikari.util.UtilityElf;
|
||||
|
||||
public class HikariConfig implements HikariConfigMBean
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HikariConfig.class);
|
||||
|
||||
private static final long CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
|
||||
private static final long VALIDATION_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
|
||||
private static final long IDLE_TIMEOUT = TimeUnit.MINUTES.toMillis(10);
|
||||
private static final long MAX_LIFETIME = TimeUnit.MINUTES.toMillis(30);
|
||||
|
||||
private static int poolNumber;
|
||||
private static boolean unitTest;
|
||||
|
||||
// Properties changeable at runtime through the MBean
|
||||
//
|
||||
private volatile long connectionTimeout;
|
||||
private volatile long validationTimeout;
|
||||
private volatile long idleTimeout;
|
||||
private volatile long leakDetectionThreshold;
|
||||
private volatile long maxLifetime;
|
||||
private volatile int maxPoolSize;
|
||||
private volatile int minIdle;
|
||||
|
||||
// Properties NOT changeable at runtime
|
||||
//
|
||||
private String catalog;
|
||||
private String connectionCustomizerClassName;
|
||||
private String connectionInitSql;
|
||||
private String connectionTestQuery;
|
||||
private String dataSourceClassName;
|
||||
private String dataSourceJndiName;
|
||||
private String driverClassName;
|
||||
private String jdbcUrl;
|
||||
private String password;
|
||||
private String poolName;
|
||||
private String transactionIsolationName;
|
||||
private String username;
|
||||
private boolean isAutoCommit;
|
||||
private boolean isReadOnly;
|
||||
private boolean isInitializationFailFast;
|
||||
private boolean isIsolateInternalQueries;
|
||||
private boolean isRegisterMbeans;
|
||||
private boolean isAllowPoolSuspension;
|
||||
private DataSource dataSource;
|
||||
private Properties dataSourceProperties;
|
||||
private ThreadFactory threadFactory;
|
||||
private Object metricRegistry;
|
||||
private Object healthCheckRegistry;
|
||||
private Properties healthCheckProperties;
|
||||
|
||||
static
|
||||
{
|
||||
JavassistProxyFactory.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public HikariConfig()
|
||||
{
|
||||
dataSourceProperties = new Properties();
|
||||
healthCheckProperties = new Properties();
|
||||
|
||||
connectionTimeout = CONNECTION_TIMEOUT;
|
||||
validationTimeout = VALIDATION_TIMEOUT;
|
||||
idleTimeout = IDLE_TIMEOUT;
|
||||
isAutoCommit = true;
|
||||
isInitializationFailFast = true;
|
||||
minIdle = -1;
|
||||
maxPoolSize = 10;
|
||||
maxLifetime = MAX_LIFETIME;
|
||||
|
||||
String systemProp = System.getProperty("hikaricp.configurationFile");
|
||||
if ( systemProp != null) {
|
||||
loadProperties(systemProp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a HikariConfig from the specified properties object.
|
||||
*
|
||||
* @param properties the name of the property file
|
||||
*/
|
||||
public HikariConfig(Properties properties)
|
||||
{
|
||||
this();
|
||||
PropertyBeanSetter.setTargetFromProperties(this, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a HikariConfig from the specified property file name. <code>propertyFileName</code>
|
||||
* will first be treated as a path in the file-system, and if that fails the
|
||||
* ClassLoader.getResourceAsStream(propertyFileName) will be tried.
|
||||
*
|
||||
* @param propertyFileName the name of the property file
|
||||
*/
|
||||
public HikariConfig(String propertyFileName)
|
||||
{
|
||||
this();
|
||||
|
||||
loadProperties(propertyFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default catalog name to be set on connections.
|
||||
*
|
||||
* @return the default catalog name
|
||||
*/
|
||||
public String getCatalog()
|
||||
{
|
||||
return catalog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default catalog name to be set on connections.
|
||||
*
|
||||
* @param catalog the catalog name, or null
|
||||
*/
|
||||
public void setCatalog(String catalog)
|
||||
{
|
||||
this.catalog = catalog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL query to be executed to test the validity of connections.
|
||||
*
|
||||
* @return the SQL query string, or null
|
||||
*/
|
||||
public String getConnectionTestQuery()
|
||||
{
|
||||
return connectionTestQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SQL query to be executed to test the validity of connections. Using
|
||||
* the JDBC4 <code>Connection.isValid()</code> method to test connection validity can
|
||||
* be more efficient on some databases and is recommended. See
|
||||
* {@link HikariConfig#setJdbc4ConnectionTest(boolean)}.
|
||||
*
|
||||
* @param connectionTestQuery a SQL query string
|
||||
*/
|
||||
public void setConnectionTestQuery(String connectionTestQuery)
|
||||
{
|
||||
this.connectionTestQuery = connectionTestQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL string that will be executed on all new connections when they are
|
||||
* created, before they are added to the pool.
|
||||
*
|
||||
* @return the SQL to execute on new connections, or null
|
||||
*/
|
||||
public String getConnectionInitSql()
|
||||
{
|
||||
return connectionInitSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the SQL string that will be executed on all new connections when they are
|
||||
* created, before they are added to the pool. If this query fails, it will be
|
||||
* treated as a failed connection attempt.
|
||||
*
|
||||
* @param connectionInitSql the SQL to execute on new connections
|
||||
*/
|
||||
public void setConnectionInitSql(String connectionInitSql)
|
||||
{
|
||||
this.connectionInitSql = connectionInitSql;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public long getConnectionTimeout()
|
||||
{
|
||||
return connectionTimeout;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setConnectionTimeout(long connectionTimeoutMs)
|
||||
{
|
||||
if (connectionTimeoutMs == 0) {
|
||||
this.connectionTimeout = Integer.MAX_VALUE;
|
||||
}
|
||||
else if (connectionTimeoutMs < 1000) {
|
||||
throw new IllegalArgumentException("connectionTimeout cannot be less than 1000ms");
|
||||
}
|
||||
else {
|
||||
this.connectionTimeout = connectionTimeoutMs;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public long getValidationTimeout()
|
||||
{
|
||||
return validationTimeout;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setValidationTimeout(long validationTimeoutMs)
|
||||
{
|
||||
if (validationTimeoutMs < 1000) {
|
||||
throw new IllegalArgumentException("validationTimeout cannot be less than 1000ms");
|
||||
}
|
||||
else {
|
||||
this.validationTimeout = validationTimeoutMs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link DataSource} that has been explicitly specified to be wrapped by the
|
||||
* pool.
|
||||
*
|
||||
* @return the {@link DataSource} instance, or null
|
||||
*/
|
||||
public DataSource getDataSource()
|
||||
{
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a {@link DataSource} for the pool to explicitly wrap. This setter is not
|
||||
* available through property file based initialization.
|
||||
*
|
||||
* @param dataSource a specific {@link DataSource} to be wrapped by the pool
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource)
|
||||
{
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
public String getDataSourceClassName()
|
||||
{
|
||||
return dataSourceClassName;
|
||||
}
|
||||
|
||||
public void setDataSourceClassName(String className)
|
||||
{
|
||||
this.dataSourceClassName = className;
|
||||
}
|
||||
|
||||
public void addDataSourceProperty(String propertyName, Object value)
|
||||
{
|
||||
dataSourceProperties.put(propertyName, value);
|
||||
}
|
||||
|
||||
public String getDataSourceJNDI()
|
||||
{
|
||||
return this.dataSourceJndiName;
|
||||
}
|
||||
|
||||
public void setDataSourceJNDI(String jndiDataSource)
|
||||
{
|
||||
this.dataSourceJndiName = jndiDataSource;
|
||||
}
|
||||
|
||||
public Properties getDataSourceProperties()
|
||||
{
|
||||
return dataSourceProperties;
|
||||
}
|
||||
|
||||
public void setDataSourceProperties(Properties dsProperties)
|
||||
{
|
||||
dataSourceProperties.putAll(dsProperties);
|
||||
}
|
||||
|
||||
public String getDriverClassName()
|
||||
{
|
||||
return driverClassName;
|
||||
}
|
||||
|
||||
public void setDriverClassName(String driverClassName)
|
||||
{
|
||||
try {
|
||||
Class<?> driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
|
||||
driverClass.newInstance();
|
||||
this.driverClassName = driverClassName;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("driverClassName specified class '" + driverClassName + "' could not be loaded", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public long getIdleTimeout()
|
||||
{
|
||||
return idleTimeout;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setIdleTimeout(long idleTimeoutMs)
|
||||
{
|
||||
if (idleTimeoutMs < 0) {
|
||||
throw new IllegalArgumentException("idleTimeout cannot be negative");
|
||||
}
|
||||
this.idleTimeout = idleTimeoutMs;
|
||||
}
|
||||
|
||||
public String getJdbcUrl()
|
||||
{
|
||||
return jdbcUrl;
|
||||
}
|
||||
|
||||
public void setJdbcUrl(String jdbcUrl)
|
||||
{
|
||||
this.jdbcUrl = jdbcUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default auto-commit behavior of connections in the pool.
|
||||
*
|
||||
* @return the default auto-commit behavior of connections
|
||||
*/
|
||||
public boolean isAutoCommit()
|
||||
{
|
||||
return isAutoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default auto-commit behavior of connections in the pool.
|
||||
*
|
||||
* @param isAutoCommit the desired auto-commit default for connections
|
||||
*/
|
||||
public void setAutoCommit(boolean isAutoCommit)
|
||||
{
|
||||
this.isAutoCommit = isAutoCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pool suspension behavior (allowed or disallowed).
|
||||
*
|
||||
* @return the pool suspension behavior
|
||||
*/
|
||||
public boolean isAllowPoolSuspension()
|
||||
{
|
||||
return isAllowPoolSuspension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not pool suspension is allowed. There is a performance
|
||||
* impact when pool suspension is enabled. Unless you need it (for a
|
||||
* redundancy system for example) do not enable it.
|
||||
*
|
||||
* @param isAllowPoolSuspension the desired pool suspension allowance
|
||||
*/
|
||||
public void setAllowPoolSuspension(boolean isAllowPoolSuspension)
|
||||
{
|
||||
this.isAllowPoolSuspension = isAllowPoolSuspension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether or not the construction of the pool should throw an exception
|
||||
* if the minimum number of connections cannot be created.
|
||||
*
|
||||
* @return whether or not initialization should fail on error immediately
|
||||
*/
|
||||
public boolean isInitializationFailFast()
|
||||
{
|
||||
return isInitializationFailFast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not the construction of the pool should throw an exception
|
||||
* if the minimum number of connections cannot be created.
|
||||
*
|
||||
* @param failFast true if the pool should fail if the minimum connections cannot be created
|
||||
*/
|
||||
public void setInitializationFailFast(boolean failFast)
|
||||
{
|
||||
isInitializationFailFast = failFast;
|
||||
}
|
||||
|
||||
public boolean isIsolateInternalQueries()
|
||||
{
|
||||
return isIsolateInternalQueries;
|
||||
}
|
||||
|
||||
public void setIsolateInternalQueries(boolean isolate)
|
||||
{
|
||||
this.isIsolateInternalQueries = isolate;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isJdbc4ConnectionTest()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setJdbc4ConnectionTest(boolean useIsValid)
|
||||
{
|
||||
// ignored deprecated property
|
||||
LOGGER.warn("The jdbcConnectionTest property is now deprecated, see the documentation for connectionTestQuery");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Codahale MetricRegistry, could be null.
|
||||
*
|
||||
* @return the codahale MetricRegistry instance
|
||||
*/
|
||||
public Object getMetricRegistry()
|
||||
{
|
||||
return metricRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Codahale MetricRegistry to use for HikariCP.
|
||||
*
|
||||
* @param metricRegistry the Codahale MetricRegistry to set
|
||||
*/
|
||||
public void setMetricRegistry(Object metricRegistry)
|
||||
{
|
||||
if (metricRegistry != null) {
|
||||
if (metricRegistry instanceof String) {
|
||||
try {
|
||||
InitialContext initCtx = new InitialContext();
|
||||
metricRegistry = (MetricRegistry) initCtx.lookup((String) metricRegistry);
|
||||
}
|
||||
catch (NamingException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(metricRegistry instanceof MetricRegistry)) {
|
||||
throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.MetricRegistry");
|
||||
}
|
||||
}
|
||||
|
||||
this.metricRegistry = metricRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Codahale HealthCheckRegistry, could be null.
|
||||
*
|
||||
* @return the Codahale HealthCheckRegistry instance
|
||||
*/
|
||||
public Object getHealthCheckRegistry()
|
||||
{
|
||||
return healthCheckRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Codahale HealthCheckRegistry to use for HikariCP.
|
||||
*
|
||||
* @param healthCheckRegistry the Codahale HealthCheckRegistry to set
|
||||
*/
|
||||
public void setHealthCheckRegistry(Object healthCheckRegistry)
|
||||
{
|
||||
if (healthCheckRegistry != null) {
|
||||
if (healthCheckRegistry instanceof String) {
|
||||
try {
|
||||
InitialContext initCtx = new InitialContext();
|
||||
healthCheckRegistry = (HealthCheckRegistry) initCtx.lookup((String) healthCheckRegistry);
|
||||
}
|
||||
catch (NamingException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(healthCheckRegistry instanceof HealthCheckRegistry)) {
|
||||
throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.health.HealthCheckRegistry");
|
||||
}
|
||||
}
|
||||
|
||||
this.healthCheckRegistry = healthCheckRegistry;
|
||||
}
|
||||
|
||||
public Properties getHealthCheckProperties()
|
||||
{
|
||||
return healthCheckProperties;
|
||||
}
|
||||
|
||||
public void setHealthCheckProperties(Properties healthCheckProperties)
|
||||
{
|
||||
this.healthCheckProperties.putAll(healthCheckProperties);
|
||||
}
|
||||
|
||||
public void addHealthCheckProperty(String key, String value)
|
||||
{
|
||||
healthCheckProperties.setProperty(key, value);
|
||||
}
|
||||
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
return isReadOnly;
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly)
|
||||
{
|
||||
this.isReadOnly = readOnly;
|
||||
}
|
||||
|
||||
public boolean isRegisterMbeans()
|
||||
{
|
||||
return isRegisterMbeans;
|
||||
}
|
||||
|
||||
public void setRegisterMbeans(boolean register)
|
||||
{
|
||||
this.isRegisterMbeans = register;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public long getLeakDetectionThreshold()
|
||||
{
|
||||
return leakDetectionThreshold;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
|
||||
{
|
||||
this.leakDetectionThreshold = leakDetectionThresholdMs;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public long getMaxLifetime()
|
||||
{
|
||||
return maxLifetime;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setMaxLifetime(long maxLifetimeMs)
|
||||
{
|
||||
this.maxLifetime = maxLifetimeMs;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getMaximumPoolSize()
|
||||
{
|
||||
return maxPoolSize;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setMaximumPoolSize(int maxPoolSize)
|
||||
{
|
||||
if (maxPoolSize < 1) {
|
||||
throw new IllegalArgumentException("maxPoolSize cannot be less than 1");
|
||||
}
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getMinimumIdle()
|
||||
{
|
||||
return minIdle;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setMinimumIdle(int minIdle)
|
||||
{
|
||||
if (minIdle < 0) {
|
||||
throw new IllegalArgumentException("minimumIdle cannot be negative");
|
||||
}
|
||||
this.minIdle = minIdle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default password to use for DataSource.getConnection(username, password) calls.
|
||||
* @return the password
|
||||
*/
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default password to use for DataSource.getConnection(username, password) calls.
|
||||
* @param password the password
|
||||
*/
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getPoolName()
|
||||
{
|
||||
return poolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the connection pool. This is primarily used for the MBean
|
||||
* to uniquely identify the pool configuration.
|
||||
*
|
||||
* @param poolName the name of the connection pool to use
|
||||
*/
|
||||
public void setPoolName(String poolName)
|
||||
{
|
||||
this.poolName = poolName;
|
||||
}
|
||||
|
||||
public String getTransactionIsolation()
|
||||
{
|
||||
return transactionIsolationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default transaction isolation level. The specified value is the
|
||||
* constant name from the <code>Connection</code> class, eg.
|
||||
* <code>TRANSACTION_REPEATABLE_READ</code>.
|
||||
*
|
||||
* @param isolationLevel the name of the isolation level
|
||||
*/
|
||||
public void setTransactionIsolation(String isolationLevel)
|
||||
{
|
||||
this.transactionIsolationName = isolationLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default username used for DataSource.getConnection(username, password) calls.
|
||||
*
|
||||
* @return the username
|
||||
*/
|
||||
public String getUsername()
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username used for DataSource.getConnection(username, password) calls.
|
||||
*
|
||||
* @param username the username
|
||||
*/
|
||||
public void setUsername(String username)
|
||||
{
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thread factory used to create threads.
|
||||
*
|
||||
* @return the thread factory (may be null, in which case the default thread factory is used)
|
||||
*/
|
||||
public ThreadFactory getThreadFactory()
|
||||
{
|
||||
return threadFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the thread factory to be used to create threads.
|
||||
*
|
||||
* @param threadFactory the thread factory (setting to null causes the default thread factory to be used)
|
||||
*/
|
||||
public void setThreadFactory(ThreadFactory threadFactory)
|
||||
{
|
||||
this.threadFactory = threadFactory;
|
||||
}
|
||||
|
||||
public void validate()
|
||||
{
|
||||
Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
validateNumerics();
|
||||
|
||||
if (connectionCustomizerClassName != null) {
|
||||
try {
|
||||
getClass().getClassLoader().loadClass(connectionCustomizerClassName);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("connectionCustomizationClass specified class '" + connectionCustomizerClassName + "' could not be loaded", e);
|
||||
connectionCustomizerClassName = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (driverClassName != null && jdbcUrl == null) {
|
||||
logger.error("when specifying driverClassName, jdbcUrl must also be specified");
|
||||
throw new IllegalStateException("when specifying driverClassName, jdbcUrl must also be specified");
|
||||
}
|
||||
else if (driverClassName != null && dataSourceClassName != null) {
|
||||
logger.error("both driverClassName and dataSourceClassName are specified, one or the other should be used");
|
||||
throw new IllegalStateException("both driverClassName and dataSourceClassName are specified, one or the other should be used");
|
||||
}
|
||||
else if (jdbcUrl != null) {
|
||||
// OK
|
||||
}
|
||||
else if (dataSource == null && dataSourceClassName == null) {
|
||||
logger.error("one of either dataSource, dataSourceClassName, or jdbcUrl and driverClassName must be specified");
|
||||
throw new IllegalArgumentException("one of either dataSource or dataSourceClassName must be specified");
|
||||
}
|
||||
else if (dataSource != null && dataSourceClassName != null) {
|
||||
logger.warn("both dataSource and dataSourceClassName are specified, ignoring dataSourceClassName");
|
||||
}
|
||||
|
||||
if (transactionIsolationName != null) {
|
||||
UtilityElf.getTransactionIsolation(transactionIsolationName);
|
||||
}
|
||||
|
||||
if (poolName == null) {
|
||||
poolName = "HikariPool-" + poolNumber++;
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled() || unitTest) {
|
||||
logConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
private void validateNumerics()
|
||||
{
|
||||
Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
if (validationTimeout > connectionTimeout && connectionTimeout != 0) {
|
||||
logger.warn("validationTimeout is greater than connectionTimeout, setting validationTimeout to connectionTimeout.");
|
||||
validationTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
if (minIdle < 0 || minIdle > maxPoolSize) {
|
||||
minIdle = maxPoolSize;
|
||||
}
|
||||
|
||||
if (maxLifetime < 0) {
|
||||
logger.error("maxLifetime cannot be negative.");
|
||||
throw new IllegalArgumentException("maxLifetime cannot be negative.");
|
||||
}
|
||||
else if (maxLifetime > 0 && maxLifetime < TimeUnit.SECONDS.toMillis(30)) {
|
||||
logger.warn("maxLifetime is less than 30000ms, using default {}ms.", MAX_LIFETIME);
|
||||
maxLifetime = MAX_LIFETIME;
|
||||
}
|
||||
|
||||
if (idleTimeout != 0 && idleTimeout < TimeUnit.SECONDS.toMillis(10)) {
|
||||
logger.warn("idleTimeout is less than 10000ms, using default {}ms.", IDLE_TIMEOUT);
|
||||
idleTimeout = IDLE_TIMEOUT;
|
||||
}
|
||||
else if (idleTimeout > maxLifetime && maxLifetime > 0) {
|
||||
logger.warn("idleTimeout is greater than maxLifetime, setting to maxLifetime.");
|
||||
idleTimeout = maxLifetime;
|
||||
}
|
||||
|
||||
if (leakDetectionThreshold != 0 && leakDetectionThreshold < TimeUnit.SECONDS.toMillis(2) && !unitTest) {
|
||||
logger.warn("leakDetectionThreshold is less than 2000ms, setting to minimum 2000ms.");
|
||||
leakDetectionThreshold = 2000L;
|
||||
}
|
||||
}
|
||||
|
||||
private void logConfiguration()
|
||||
{
|
||||
LOGGER.debug("HikariCP pool {} configuration:", poolName);
|
||||
final Set<String> propertyNames = new TreeSet<String>(PropertyBeanSetter.getPropertyNames(HikariConfig.class));
|
||||
for (String prop : propertyNames) {
|
||||
try {
|
||||
Object value = PropertyBeanSetter.getProperty(prop, this);
|
||||
if ("dataSourceProperties".equals(prop)) {
|
||||
Properties dsProps = PropertyBeanSetter.copyProperties(dataSourceProperties);
|
||||
dsProps.setProperty("password", "<masked>");
|
||||
value = dsProps;
|
||||
}
|
||||
value = (prop.contains("password") ? "<masked>" : value);
|
||||
LOGGER.debug((prop + "................................................").substring(0, 32) + (value != null ? value : ""));
|
||||
}
|
||||
catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadProperties(String propertyFileName)
|
||||
{
|
||||
final File propFile = new File(propertyFileName);
|
||||
try (final InputStream is = propFile.isFile() ? new FileInputStream(propFile) : this.getClass().getResourceAsStream(propertyFileName)) {
|
||||
if (is != null) {
|
||||
Properties props = new Properties();
|
||||
props.load(is);
|
||||
PropertyBeanSetter.setTargetFromProperties(this, props);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Property file " + propertyFileName + " was not found.");
|
||||
}
|
||||
}
|
||||
catch (IOException io) {
|
||||
throw new RuntimeException("Error loading properties file", io);
|
||||
}
|
||||
}
|
||||
|
||||
public void copyState(HikariConfig other)
|
||||
{
|
||||
for (Field field : HikariConfig.class.getDeclaredFields()) {
|
||||
if (!Modifier.isFinal(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
field.set(other, field.get(this));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Exception copying HikariConfig state: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Brett Wooldridge
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zaxxer.hikari.pool;
|
||||
|
||||
/**
|
||||
* A custom exception thrown if pool initialization fails.
|
||||
*
|
||||
* @author Brett Wooldridge
|
||||
*/
|
||||
public class PoolInitializationException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 929872118275916520L;
|
||||
|
||||
/**
|
||||
* Construct an exception, possibly wrapping the provided Throwable as the cause.
|
||||
*/
|
||||
public PoolInitializationException(Throwable t)
|
||||
{
|
||||
super("Exception during pool initialization", t);
|
||||
}
|
||||
}
|
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Brett Wooldridge
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zaxxer.hikari.proxy;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
import javassist.CtNewMethod;
|
||||
import javassist.LoaderClassPath;
|
||||
import javassist.Modifier;
|
||||
import javassist.NotFoundException;
|
||||
|
||||
import com.zaxxer.hikari.util.ClassLoaderUtils;
|
||||
|
||||
/**
|
||||
* This class generates the proxy objects for {@link Connection}, {@link Statement},
|
||||
* {@link PreparedStatement}, and {@link CallableStatement}. Additionally it injects
|
||||
* method bodies into the {@link ProxyFactory} class methods that can instantiate
|
||||
* instances of the generated proxies.
|
||||
*
|
||||
* @author Brett Wooldridge
|
||||
*/
|
||||
public final class JavassistProxyFactory
|
||||
{
|
||||
private ClassPool classPool;
|
||||
|
||||
static {
|
||||
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(JavassistProxyFactory.class.getClassLoader());
|
||||
|
||||
JavassistProxyFactory proxyFactoryFactory = new JavassistProxyFactory();
|
||||
proxyFactoryFactory.modifyProxyFactory();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Fatal exception during proxy generation", e);
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(contextClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply invoking this method causes the initialization of this class. All work
|
||||
* by this class is performed in static initialization.
|
||||
*/
|
||||
public static void initialize()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
private JavassistProxyFactory() throws Exception
|
||||
{
|
||||
classPool = new ClassPool();
|
||||
classPool.importPackage("java.sql");
|
||||
classPool.appendClassPath(new LoaderClassPath(this.getClass().getClassLoader()));
|
||||
|
||||
// Cast is not needed for these
|
||||
String methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { throw checkException(e); } }";
|
||||
generateProxyClass(Connection.class, ConnectionProxy.class, methodBody);
|
||||
generateProxyClass(Statement.class, StatementProxy.class, methodBody);
|
||||
generateProxyClass(ResultSet.class, ResultSetProxy.class, methodBody);
|
||||
|
||||
// For these we have to cast the delegate
|
||||
methodBody = "{ try { return ((cast) delegate).method($$); } catch (SQLException e) { throw checkException(e); } }";
|
||||
generateProxyClass(PreparedStatement.class, PreparedStatementProxy.class, methodBody);
|
||||
generateProxyClass(CallableStatement.class, CallableStatementProxy.class, methodBody);
|
||||
}
|
||||
|
||||
private void modifyProxyFactory() throws Exception
|
||||
{
|
||||
String packageName = JavassistProxyFactory.class.getPackage().getName();
|
||||
CtClass proxyCt = classPool.getCtClass("com.zaxxer.hikari.proxy.ProxyFactory");
|
||||
for (CtMethod method : proxyCt.getMethods()) {
|
||||
switch (method.getName()) {
|
||||
case "getProxyConnection":
|
||||
method.setBody("{return new " + packageName + ".ConnectionJavassistProxy($$);}");
|
||||
break;
|
||||
case "getProxyStatement":
|
||||
method.setBody("{return new " + packageName + ".StatementJavassistProxy($$);}");
|
||||
break;
|
||||
case "getProxyPreparedStatement":
|
||||
method.setBody("{return new " + packageName + ".PreparedStatementJavassistProxy($$);}");
|
||||
break;
|
||||
case "getProxyCallableStatement":
|
||||
method.setBody("{return new " + packageName + ".CallableStatementJavassistProxy($$);}");
|
||||
break;
|
||||
case "getProxyResultSet":
|
||||
method.setBody("{return new " + packageName + ".ResultSetJavassistProxy($$);}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
proxyCt.toClass(classPool.getClassLoader(), getClass().getProtectionDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Javassist Proxy Classes
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Class<T> generateProxyClass(Class<T> primaryInterface, Class<?> superClass, String methodBody) throws Exception
|
||||
{
|
||||
// Make a new class that extends one of the JavaProxy classes (ie. superClass); use the name to XxxJavassistProxy instead of XxxProxy
|
||||
String superClassName = superClass.getName();
|
||||
CtClass superClassCt = classPool.getCtClass(superClassName);
|
||||
CtClass targetCt = classPool.makeClass(superClassName.replace("Proxy", "JavassistProxy"), superClassCt);
|
||||
targetCt.setModifiers(Modifier.FINAL);
|
||||
|
||||
// Make a set of method signatures we inherit implementation for, so we don't generate delegates for these
|
||||
Set<String> superSigs = new HashSet<String>();
|
||||
for (CtMethod method : superClassCt.getMethods()) {
|
||||
if ((method.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
|
||||
superSigs.add(method.getName() + method.getSignature());
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> methods = new HashSet<String>();
|
||||
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(primaryInterface);
|
||||
for (Class<?> intf : interfaces) {
|
||||
CtClass intfCt = classPool.getCtClass(intf.getName());
|
||||
targetCt.addInterface(intfCt);
|
||||
for (CtMethod intfMethod : intfCt.getDeclaredMethods()) {
|
||||
final String signature = intfMethod.getName() + intfMethod.getSignature();
|
||||
|
||||
// don't generate delegates for methods we override
|
||||
if (superSigs.contains(signature)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore already added methods that come from other interfaces
|
||||
if (methods.contains(signature)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore default methods (only for Jre8 or later)
|
||||
if (isDefaultMethod(intf, intfCt, intfMethod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track what methods we've added
|
||||
methods.add(signature);
|
||||
|
||||
// Clone the method we want to inject into
|
||||
CtMethod method = CtNewMethod.copy(intfMethod, targetCt, null);
|
||||
|
||||
String modifiedBody = methodBody;
|
||||
|
||||
// If the super-Proxy has concrete methods (non-abstract), transform the call into a simple super.method() call
|
||||
CtMethod superMethod = superClassCt.getMethod(intfMethod.getName(), intfMethod.getSignature());
|
||||
if ((superMethod.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT) {
|
||||
modifiedBody = modifiedBody.replace("((cast) ", "");
|
||||
modifiedBody = modifiedBody.replace("delegate", "super");
|
||||
modifiedBody = modifiedBody.replace("super)", "super");
|
||||
}
|
||||
|
||||
modifiedBody = modifiedBody.replace("cast", primaryInterface.getName());
|
||||
|
||||
// Generate a method that simply invokes the same method on the delegate
|
||||
if (isThrowsSqlException(intfMethod)) {
|
||||
modifiedBody = modifiedBody.replace("method", method.getName());
|
||||
}
|
||||
else {
|
||||
modifiedBody = "{ return ((cast) delegate).method($$); }".replace("method", method.getName()).replace("cast", primaryInterface.getName());
|
||||
}
|
||||
|
||||
if (method.getReturnType() == CtClass.voidType) {
|
||||
modifiedBody = modifiedBody.replace("return", "");
|
||||
}
|
||||
|
||||
method.setBody(modifiedBody);
|
||||
targetCt.addMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
return targetCt.toClass(classPool.getClassLoader(), getClass().getProtectionDomain());
|
||||
}
|
||||
|
||||
private boolean isThrowsSqlException(CtMethod method)
|
||||
{
|
||||
try {
|
||||
for (CtClass clazz : method.getExceptionTypes()) {
|
||||
if (clazz.getSimpleName().equals("SQLException")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NotFoundException e) {
|
||||
// fall thru
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isDefaultMethod(Class<?> intf, CtClass intfCt, CtMethod intfMethod) throws Exception
|
||||
{
|
||||
List<Class<?>> paramTypes = new ArrayList<Class<?>>();
|
||||
|
||||
for (CtClass pt : intfMethod.getParameterTypes()) {
|
||||
paramTypes.add(toJavaClass(pt));
|
||||
}
|
||||
|
||||
return intf.getDeclaredMethod(intfMethod.getName(), paramTypes.toArray(new Class[0])).toString().contains("default ");
|
||||
}
|
||||
|
||||
private Class<?> toJavaClass(CtClass cls) throws Exception
|
||||
{
|
||||
if (cls.getName().endsWith("[]")) {
|
||||
return Array.newInstance(toJavaClass(cls.getName().replace("[]", "")), 0).getClass();
|
||||
}
|
||||
else {
|
||||
return toJavaClass(cls.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> toJavaClass(String cn) throws Exception
|
||||
{
|
||||
switch (cn) {
|
||||
case "int":
|
||||
return int.class;
|
||||
case "long":
|
||||
return long.class;
|
||||
case "short":
|
||||
return short.class;
|
||||
case "byte":
|
||||
return byte.class;
|
||||
case "float":
|
||||
return float.class;
|
||||
case "double":
|
||||
return double.class;
|
||||
case "boolean":
|
||||
return boolean.class;
|
||||
case "char":
|
||||
return char.class;
|
||||
case "void":
|
||||
return void.class;
|
||||
default:
|
||||
return Class.forName(cn);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2013, 2014 Brett Wooldridge
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zaxxer.hikari;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.SortedMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.MetricFilter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.Timer;
|
||||
import com.codahale.metrics.health.HealthCheck.Result;
|
||||
import com.codahale.metrics.health.HealthCheckRegistry;
|
||||
import com.zaxxer.hikari.util.UtilityElf;
|
||||
|
||||
/**
|
||||
* Test HikariCP/CodaHale metrics integration.
|
||||
*
|
||||
* @author Brett Wooldridge
|
||||
*/
|
||||
public class TestMetrics
|
||||
{
|
||||
@Test
|
||||
public void testMetricWait() throws SQLException
|
||||
{
|
||||
MetricRegistry metricRegistry = new MetricRegistry();
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setMinimumIdle(1);
|
||||
config.setMaximumPoolSize(1);
|
||||
config.setMetricRegistry(metricRegistry);
|
||||
config.setInitializationFailFast(false);
|
||||
config.setPoolName("test");
|
||||
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
|
||||
|
||||
HikariDataSource ds = new HikariDataSource(config);
|
||||
try {
|
||||
ds.getConnection().close();
|
||||
|
||||
Timer timer = metricRegistry.getTimers(new MetricFilter() {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean matches(String name, Metric metric)
|
||||
{
|
||||
return "test.pool.Wait".equals(MetricRegistry.name("test", "pool", "Wait"));
|
||||
}
|
||||
}).values().iterator().next();
|
||||
|
||||
Assert.assertEquals(1, timer.getCount());
|
||||
Assert.assertTrue(timer.getMeanRate() > 0.0);
|
||||
}
|
||||
finally {
|
||||
ds.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetricUsage() throws SQLException
|
||||
{
|
||||
MetricRegistry metricRegistry = new MetricRegistry();
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setMinimumIdle(1);
|
||||
config.setMaximumPoolSize(1);
|
||||
config.setMetricRegistry(metricRegistry);
|
||||
config.setInitializationFailFast(false);
|
||||
config.setPoolName("test");
|
||||
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
|
||||
|
||||
HikariDataSource ds = new HikariDataSource(config);
|
||||
try {
|
||||
Connection connection = ds.getConnection();
|
||||
UtilityElf.quietlySleep(250L);
|
||||
connection.close();
|
||||
|
||||
Histogram histo = metricRegistry.getHistograms(new MetricFilter() {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean matches(String name, Metric metric)
|
||||
{
|
||||
return "test.pool.Usage".equals(MetricRegistry.name("test", "pool", "Usage"));
|
||||
}
|
||||
}).values().iterator().next();
|
||||
|
||||
Assert.assertEquals(1, histo.getCount());
|
||||
double seventyFifth = histo.getSnapshot().get75thPercentile();
|
||||
Assert.assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
|
||||
}
|
||||
finally {
|
||||
ds.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHealthChecks() throws Exception
|
||||
{
|
||||
MetricRegistry metricRegistry = new MetricRegistry();
|
||||
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setMaximumPoolSize(10);
|
||||
config.setMetricRegistry(metricRegistry);
|
||||
config.setHealthCheckRegistry(healthRegistry);
|
||||
config.setPoolName("test");
|
||||
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
|
||||
config.addHealthCheckProperty("connectivityCheckTimeoutMs", "1000");
|
||||
config.addHealthCheckProperty("expected99thPercentileMs", "10");
|
||||
|
||||
HikariDataSource ds = new HikariDataSource(config);
|
||||
try {
|
||||
UtilityElf.quietlySleep(TimeUnit.SECONDS.toMillis(2));
|
||||
|
||||
Connection connection = ds.getConnection();
|
||||
connection.close();
|
||||
|
||||
connection = ds.getConnection();
|
||||
connection.close();
|
||||
|
||||
SortedMap<String, Result> healthChecks = healthRegistry.runHealthChecks();
|
||||
|
||||
Result connectivityResult = healthChecks.get("test.pool.ConnectivityCheck");
|
||||
Assert.assertTrue(connectivityResult.isHealthy());
|
||||
|
||||
Result slaResult = healthChecks.get("test.pool.Connection99Percent");
|
||||
Assert.assertTrue(slaResult.isHealthy());
|
||||
}
|
||||
finally {
|
||||
ds.close();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue