|
|
|
@ -3,7 +3,9 @@ package com.zaxxer.hikari.pool;
|
|
|
|
|
import static com.zaxxer.hikari.util.UtilityElf.createInstance;
|
|
|
|
|
|
|
|
|
|
import java.lang.management.ManagementFactory;
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
|
import java.sql.Connection;
|
|
|
|
|
import java.sql.ResultSet;
|
|
|
|
|
import java.sql.SQLException;
|
|
|
|
|
import java.sql.Statement;
|
|
|
|
|
import java.util.Properties;
|
|
|
|
@ -29,22 +31,41 @@ public final class PoolElf
|
|
|
|
|
{
|
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(PoolElf.class);
|
|
|
|
|
|
|
|
|
|
private int networkTimeout;
|
|
|
|
|
private int transactionIsolation;
|
|
|
|
|
private long validationTimeout;
|
|
|
|
|
private boolean isNetworkTimeoutSupported;
|
|
|
|
|
private boolean isQueryTimeoutSupported;
|
|
|
|
|
private Executor netTimeoutExecutor;
|
|
|
|
|
|
|
|
|
|
private final HikariConfig config;
|
|
|
|
|
private final String poolName;
|
|
|
|
|
private final String catalog;
|
|
|
|
|
private final boolean isReadOnly;
|
|
|
|
|
private final boolean isAutoCommit;
|
|
|
|
|
private final boolean isUseJdbc4Validation;
|
|
|
|
|
private final boolean isIsolateInternalQueries;
|
|
|
|
|
|
|
|
|
|
private volatile boolean isValidChecked;
|
|
|
|
|
private volatile boolean isValidSupported;
|
|
|
|
|
private boolean isNetworkTimeoutSupported;
|
|
|
|
|
private boolean isQueryTimeoutSupported;
|
|
|
|
|
|
|
|
|
|
public PoolElf(final HikariConfig configuration)
|
|
|
|
|
{
|
|
|
|
|
this.config = configuration;
|
|
|
|
|
this.poolName = configuration.getPoolName();
|
|
|
|
|
|
|
|
|
|
this.networkTimeout = -1;
|
|
|
|
|
this.catalog = config.getCatalog();
|
|
|
|
|
this.isReadOnly = config.isReadOnly();
|
|
|
|
|
this.isAutoCommit = config.isAutoCommit();
|
|
|
|
|
this.validationTimeout = config.getValidationTimeout();
|
|
|
|
|
this.transactionIsolation = getTransactionIsolation(config.getTransactionIsolation());
|
|
|
|
|
|
|
|
|
|
this.isValidSupported = true;
|
|
|
|
|
this.isNetworkTimeoutSupported = true;
|
|
|
|
|
this.isQueryTimeoutSupported = true;
|
|
|
|
|
this.isNetworkTimeoutSupported = true;
|
|
|
|
|
this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;
|
|
|
|
|
this.isIsolateInternalQueries = config.isIsolateInternalQueries();
|
|
|
|
|
this.poolName = config.getPoolName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -74,12 +95,33 @@ public final class PoolElf
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the int value of a transaction isolation level by name.
|
|
|
|
|
*
|
|
|
|
|
* @param transactionIsolationName the name of the transaction isolation level
|
|
|
|
|
* @return the int value of the isolation level or -1
|
|
|
|
|
*/
|
|
|
|
|
public static int getTransactionIsolation(final String transactionIsolationName)
|
|
|
|
|
{
|
|
|
|
|
if (transactionIsolationName != null) {
|
|
|
|
|
try {
|
|
|
|
|
Field field = Connection.class.getField(transactionIsolationName);
|
|
|
|
|
return field.getInt(null);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e) {
|
|
|
|
|
throw new IllegalArgumentException("Invalid transaction isolation value: " + transactionIsolationName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create/initialize the underlying DataSource.
|
|
|
|
|
*
|
|
|
|
|
* @return a DataSource instance
|
|
|
|
|
*/
|
|
|
|
|
public DataSource initializeDataSource()
|
|
|
|
|
DataSource initializeDataSource()
|
|
|
|
|
{
|
|
|
|
|
final String jdbcUrl = config.getJdbcUrl();
|
|
|
|
|
final String username = config.getUsername();
|
|
|
|
@ -109,6 +151,7 @@ public final class PoolElf
|
|
|
|
|
* Setup a connection initial state.
|
|
|
|
|
*
|
|
|
|
|
* @param connection a Connection
|
|
|
|
|
* @param connectionTimeout
|
|
|
|
|
* @param initSql
|
|
|
|
|
* @param isAutoCommit auto-commit state
|
|
|
|
|
* @param isReadOnly read-only state
|
|
|
|
@ -116,18 +159,161 @@ public final class PoolElf
|
|
|
|
|
* @param catalog default catalog
|
|
|
|
|
* @throws SQLException thrown from driver
|
|
|
|
|
*/
|
|
|
|
|
public void setupConnection(final Connection connection, final String initSql, final boolean isAutoCommit, final boolean isReadOnly, final int transactionIsolation, final String catalog) throws SQLException
|
|
|
|
|
void setupConnection(final Connection connection, final long connectionTimeout) throws SQLException
|
|
|
|
|
{
|
|
|
|
|
if (isUseJdbc4Validation && !isJdbc4ValidationSupported(connection)) {
|
|
|
|
|
throw new SQLException("JDBC4 Connection.isValid() method not supported, connection test query must be configured");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
networkTimeout = (networkTimeout < 0 ? getAndSetNetworkTimeout(connection, connectionTimeout) : networkTimeout);
|
|
|
|
|
transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation);
|
|
|
|
|
|
|
|
|
|
connection.setAutoCommit(isAutoCommit);
|
|
|
|
|
connection.setReadOnly(isReadOnly);
|
|
|
|
|
|
|
|
|
|
if (transactionIsolation != connection.getTransactionIsolation()) {
|
|
|
|
|
connection.setTransactionIsolation(transactionIsolation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (catalog != null) {
|
|
|
|
|
connection.setCatalog(catalog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
executeSql(connection, initSql, isAutoCommit);
|
|
|
|
|
executeSql(connection, config.getConnectionInitSql(), isAutoCommit);
|
|
|
|
|
|
|
|
|
|
setNetworkTimeout(connection, networkTimeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check whether the connection is alive or not.
|
|
|
|
|
*
|
|
|
|
|
* @param connection the connection to test
|
|
|
|
|
* @return true if the connection is alive, false if it is not alive or we timed out
|
|
|
|
|
*/
|
|
|
|
|
boolean isConnectionAlive(final Connection connection)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
int timeoutSec = (int) TimeUnit.MILLISECONDS.toSeconds(validationTimeout);
|
|
|
|
|
|
|
|
|
|
if (isUseJdbc4Validation) {
|
|
|
|
|
return connection.isValid(timeoutSec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
networkTimeout = getAndSetNetworkTimeout(connection, validationTimeout);
|
|
|
|
|
|
|
|
|
|
try (Statement statement = connection.createStatement()) {
|
|
|
|
|
setQueryTimeout(statement, timeoutSec);
|
|
|
|
|
try (ResultSet rs = statement.executeQuery(config.getConnectionTestQuery())) {
|
|
|
|
|
/* auto close */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isIsolateInternalQueries && !isAutoCommit) {
|
|
|
|
|
connection.rollback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setNetworkTimeout(connection, networkTimeout);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e) {
|
|
|
|
|
LOGGER.warn("Exception during alive check, Connection ({}) declared dead.", connection, e);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void resetConnectionState(final PoolBagEntry poolEntry) throws SQLException
|
|
|
|
|
{
|
|
|
|
|
if (poolEntry.isReadOnly != isReadOnly) {
|
|
|
|
|
poolEntry.connection.setReadOnly(isReadOnly);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (poolEntry.isAutoCommit != isAutoCommit) {
|
|
|
|
|
poolEntry.connection.setAutoCommit(isAutoCommit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (poolEntry.transactionIsolation != transactionIsolation) {
|
|
|
|
|
poolEntry.connection.setTransactionIsolation(transactionIsolation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final String currentCatalog = poolEntry.catalog;
|
|
|
|
|
if ((currentCatalog != null && !currentCatalog.equals(catalog)) || (currentCatalog == null && catalog != null)) {
|
|
|
|
|
poolEntry.connection.setCatalog(catalog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (poolEntry.networkTimeout != networkTimeout) {
|
|
|
|
|
setNetworkTimeout(poolEntry.connection, networkTimeout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void resetPoolEntry(final PoolBagEntry poolEntry)
|
|
|
|
|
{
|
|
|
|
|
poolEntry.setCatalog(catalog);
|
|
|
|
|
poolEntry.setReadOnly(isReadOnly);
|
|
|
|
|
poolEntry.setAutoCommit(isAutoCommit);
|
|
|
|
|
poolEntry.setNetworkTimeout(networkTimeout);
|
|
|
|
|
poolEntry.setTransactionIsolation(transactionIsolation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setValidationTimeout(final long validationTimeout)
|
|
|
|
|
{
|
|
|
|
|
this.validationTimeout = validationTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Register MBeans for HikariConfig and HikariPool.
|
|
|
|
|
*
|
|
|
|
|
* @param configuration a HikariConfig instance
|
|
|
|
|
* @param pool a HikariPool instance
|
|
|
|
|
*/
|
|
|
|
|
void registerMBeans(final HikariPool pool)
|
|
|
|
|
{
|
|
|
|
|
if (!config.isRegisterMbeans()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
|
|
|
|
|
|
|
|
|
final ObjectName beanConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + poolName + ")");
|
|
|
|
|
final ObjectName beanPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")");
|
|
|
|
|
if (!mBeanServer.isRegistered(beanConfigName)) {
|
|
|
|
|
mBeanServer.registerMBean(config, beanConfigName);
|
|
|
|
|
mBeanServer.registerMBean(pool, beanPoolName);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LOGGER.error("You cannot use the same pool name 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
|
|
|
|
|
*/
|
|
|
|
|
void unregisterMBeans()
|
|
|
|
|
{
|
|
|
|
|
if (!config.isRegisterMbeans()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
|
|
|
|
|
|
|
|
|
final ObjectName beanConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + poolName + ")");
|
|
|
|
|
final ObjectName beanPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")");
|
|
|
|
|
if (mBeanServer.isRegistered(beanConfigName)) {
|
|
|
|
|
mBeanServer.unregisterMBean(beanConfigName);
|
|
|
|
|
mBeanServer.unregisterMBean(beanPoolName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e) {
|
|
|
|
|
LOGGER.warn("Unable to unregister management beans.", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -136,7 +322,7 @@ public final class PoolElf
|
|
|
|
|
* @param connection a Connection to check
|
|
|
|
|
* @return true if JDBC 4.1 compliance, false otherwise
|
|
|
|
|
*/
|
|
|
|
|
public boolean isJdbc4ValidationSupported(final Connection connection)
|
|
|
|
|
private boolean isJdbc4ValidationSupported(final Connection connection)
|
|
|
|
|
{
|
|
|
|
|
if (!isValidChecked) {
|
|
|
|
|
try {
|
|
|
|
@ -161,7 +347,7 @@ public final class PoolElf
|
|
|
|
|
* @param statement a statement to set the query timeout on
|
|
|
|
|
* @param timeoutSec the number of seconds before timeout
|
|
|
|
|
*/
|
|
|
|
|
public void setQueryTimeout(final Statement statement, final int timeoutSec)
|
|
|
|
|
private void setQueryTimeout(final Statement statement, final int timeoutSec)
|
|
|
|
|
{
|
|
|
|
|
if (isQueryTimeoutSupported) {
|
|
|
|
|
try {
|
|
|
|
@ -182,7 +368,7 @@ public final class PoolElf
|
|
|
|
|
* @param timeoutMs the number of milliseconds before timeout
|
|
|
|
|
* @return the pre-existing network timeout value
|
|
|
|
|
*/
|
|
|
|
|
public int getAndSetNetworkTimeout(final Connection connection, final long timeoutMs)
|
|
|
|
|
private int getAndSetNetworkTimeout(final Connection connection, final long timeoutMs)
|
|
|
|
|
{
|
|
|
|
|
if (isNetworkTimeoutSupported) {
|
|
|
|
|
try {
|
|
|
|
@ -207,69 +393,13 @@ public final class PoolElf
|
|
|
|
|
* @param timeoutMs the number of milliseconds before timeout
|
|
|
|
|
* @throws SQLException throw if the connection.setNetworkTimeout() call throws
|
|
|
|
|
*/
|
|
|
|
|
public void setNetworkTimeout(final Connection connection, final long timeoutMs) throws SQLException
|
|
|
|
|
private void setNetworkTimeout(final Connection connection, final long timeoutMs) throws SQLException
|
|
|
|
|
{
|
|
|
|
|
if (isNetworkTimeoutSupported) {
|
|
|
|
|
connection.setNetworkTimeout(netTimeoutExecutor, (int) timeoutMs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Register MBeans for HikariConfig and HikariPool.
|
|
|
|
|
*
|
|
|
|
|
* @param configuration a HikariConfig instance
|
|
|
|
|
* @param pool a HikariPool instance
|
|
|
|
|
*/
|
|
|
|
|
void registerMBeans(final HikariPool pool)
|
|
|
|
|
{
|
|
|
|
|
if (!config.isRegisterMbeans()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
|
|
|
|
|
|
|
|
|
final ObjectName beanConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + poolName + ")");
|
|
|
|
|
final ObjectName beanPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")");
|
|
|
|
|
if (!mBeanServer.isRegistered(beanConfigName)) {
|
|
|
|
|
mBeanServer.registerMBean(config, beanConfigName);
|
|
|
|
|
mBeanServer.registerMBean(pool, beanPoolName);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LOGGER.error("You cannot use the same pool name 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
|
|
|
|
|
*/
|
|
|
|
|
void unregisterMBeans()
|
|
|
|
|
{
|
|
|
|
|
if (!config.isRegisterMbeans()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
|
|
|
|
|
|
|
|
|
final ObjectName beanConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + poolName + ")");
|
|
|
|
|
final ObjectName beanPoolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + poolName + ")");
|
|
|
|
|
if (mBeanServer.isRegistered(beanConfigName)) {
|
|
|
|
|
mBeanServer.unregisterMBean(beanConfigName);
|
|
|
|
|
mBeanServer.unregisterMBean(beanPoolName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e) {
|
|
|
|
|
LOGGER.warn("Unable to unregister management beans.", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute the user-specified init SQL.
|
|
|
|
|
*
|
|
|
|
|