diff --git a/hikaricp-common/src/main/java/com/zaxxer/hikari/AbstractHikariConfig.java b/hikaricp-common/src/main/java/com/zaxxer/hikari/AbstractHikariConfig.java new file mode 100644 index 00000000..adf84691 --- /dev/null +++ b/hikaricp-common/src/main/java/com/zaxxer/hikari/AbstractHikariConfig.java @@ -0,0 +1,774 @@ +/* + * 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.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.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.zaxxer.hikari.metrics.CodaHaleShim; +import com.zaxxer.hikari.proxy.JavassistProxyFactory; +import com.zaxxer.hikari.util.PoolUtilities; +import com.zaxxer.hikari.util.PropertyBeanSetter; + +public abstract class AbstractHikariConfig 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; + private static boolean unitTest; + + // 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 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 IConnectionCustomizer customizer; + private ThreadFactory threadFactory; + private Object metricRegistry; + + static { + JavassistProxyFactory.initialize(); + CodaHaleShim.initialize(); + } + + /** + * Default constructor + */ + public AbstractHikariConfig() + { + dataSourceProperties = new Properties(); + + connectionTimeout = CONNECTION_TIMEOUT; + idleTimeout = IDLE_TIMEOUT; + isAutoCommit = true; + isInitializationFailFast = true; + minIdle = -1; + maxPoolSize = 10; + maxLifetime = MAX_LIFETIME; + customizer = new IConnectionCustomizer() { + @Override + public void customize(Connection connection) throws SQLException + { + } + }; + + 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 AbstractHikariConfig(Properties properties) + { + this(); + PropertyBeanSetter.setTargetFromProperties(this, properties); + } + + /** + * Construct a HikariConfig from the specified property file name. propertyFileName + * 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 AbstractHikariConfig(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 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 Connection.isValid() 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) + { + 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 && !metricRegistry.getClass().getName().contains("MetricRegistry")) { + throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.MetricRegistry"); + } + this.metricRegistry = metricRegistry; + } + + 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 Connection class, eg. + * TRANSACTION_REPEATABLE_READ. + * + * @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) { + PoolUtilities.getTransactionIsolation(transactionIsolationName); + } + + if (poolName == null) { + poolName = "HikariPool-" + poolNumber++; + } + + if (LOGGER.isDebugEnabled() || unitTest) { + 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."); + } + + 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(120)) { + logger.warn("maxLifetime is less than 120000ms, using default {}ms.", MAX_LIFETIME); + maxLifetime = MAX_LIFETIME; + } + + if (idleTimeout != 0 && idleTimeout < TimeUnit.SECONDS.toMillis(30)) { + logger.warn("idleTimeout is less than 30000ms, 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 propertyNames = new TreeSet(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", ""); + value = dsProps; + } + value = (prop.contains("password") ? "" : value); + LOGGER.debug((prop + "................................................").substring(0, 32) + (value != null ? value : "")); + } + catch (Exception e) { + continue; + } + } + } + + abstract protected void loadProperties(String propertyFileName); + + void copyState(AbstractHikariConfig other) + { + for (Field field : AbstractHikariConfig.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); + } + } + } + } +} diff --git a/hikaricp-common/src/test/java/com/zaxxer/hikari/TestElf.java b/hikaricp-common/src/test/java/com/zaxxer/hikari/TestElf.java index 31aa088a..a69dca67 100644 --- a/hikaricp-common/src/test/java/com/zaxxer/hikari/TestElf.java +++ b/hikaricp-common/src/test/java/com/zaxxer/hikari/TestElf.java @@ -79,7 +79,7 @@ public final class TestElf public static void setConfigUnitTest(boolean unitTest) { try { - Field field = HikariConfig.class.getDeclaredField("unitTest"); + Field field = AbstractHikariConfig.class.getDeclaredField("unitTest"); field.setAccessible(true); field.setBoolean(null, unitTest); } diff --git a/hikaricp-common/src/test/java/com/zaxxer/hikari/TestValidation.java b/hikaricp-common/src/test/java/com/zaxxer/hikari/TestValidation.java index 446bbf77..49c3504f 100644 --- a/hikaricp-common/src/test/java/com/zaxxer/hikari/TestValidation.java +++ b/hikaricp-common/src/test/java/com/zaxxer/hikari/TestValidation.java @@ -276,7 +276,7 @@ public class TestValidation config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); config.setLeakDetectionThreshold(1000L); config.validate(); - Assert.assertEquals(0, config.getLeakDetectionThreshold()); + Assert.assertEquals(2000L, config.getLeakDetectionThreshold()); } catch (IllegalArgumentException ise) { // pass diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/HikariConfig.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/HikariConfig.java index 8a4c588c..a765b10b 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -20,102 +20,18 @@ 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.sql.Connection; -import java.sql.SQLException; import java.util.Properties; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import javax.sql.DataSource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.zaxxer.hikari.metrics.CodaHaleShim; -import com.zaxxer.hikari.proxy.JavassistProxyFactory; -import com.zaxxer.hikari.util.PoolUtilities; import com.zaxxer.hikari.util.PropertyBeanSetter; -public class HikariConfig implements HikariConfigMBean +public class HikariConfig extends AbstractHikariConfig { - 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; - private static boolean unitTest; - - // 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 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 IConnectionCustomizer customizer; - private ThreadFactory threadFactory; - private Object metricRegistry; - - static { - JavassistProxyFactory.initialize(); - CodaHaleShim.initialize(); - } - /** * Default constructor */ public HikariConfig() { - dataSourceProperties = new Properties(); - - connectionTimeout = CONNECTION_TIMEOUT; - idleTimeout = IDLE_TIMEOUT; - isAutoCommit = true; - isInitializationFailFast = true; - minIdle = -1; - maxPoolSize = 10; - maxLifetime = MAX_LIFETIME; - customizer = new IConnectionCustomizer() { - @Override - public void customize(Connection connection) throws SQLException - { - } - }; - - String systemProp = System.getProperty("hikaricp.configurationFile"); - if ( systemProp != null) { - loadProperties(systemProp); - } + super(); } /** @@ -125,8 +41,7 @@ public class HikariConfig implements HikariConfigMBean */ public HikariConfig(Properties properties) { - this(); - PropertyBeanSetter.setTargetFromProperties(this, properties); + super(properties); } /** @@ -138,628 +53,10 @@ public class HikariConfig implements HikariConfigMBean */ 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 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 Connection.isValid() 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) - { - 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; + super(propertyFileName); } - /** - * 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 && !metricRegistry.getClass().getName().contains("MetricRegistry")) { - throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.MetricRegistry"); - } - this.metricRegistry = metricRegistry; - } - - 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 Connection class, eg. - * TRANSACTION_REPEATABLE_READ. - * - * @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) { - PoolUtilities.getTransactionIsolation(transactionIsolationName); - } - - if (poolName == null) { - poolName = "HikariPool-" + poolNumber++; - } - - if (LOGGER.isDebugEnabled() || unitTest) { - 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."); - } - - 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(120)) { - logger.warn("maxLifetime is less than 120000ms, using default {}ms.", MAX_LIFETIME); - maxLifetime = MAX_LIFETIME; - } - - if (idleTimeout != 0 && idleTimeout < TimeUnit.SECONDS.toMillis(30)) { - logger.warn("idleTimeout is less than 30000ms, 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(10) && !unitTest) { - logger.warn("leakDetectionThreshold is less than 10000ms, disabling leak detection."); - leakDetectionThreshold = 0; - } - } - - private void logConfiguration() - { - LOGGER.debug("HikariCP pool {} configuration:", poolName); - final Set propertyNames = new TreeSet(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", ""); - value = dsProps; - } - value = (prop.contains("password") ? "" : value); - LOGGER.debug((prop + "................................................").substring(0, 32) + (value != null ? value : "")); - } - catch (Exception e) { - continue; - } - } - } - - private void loadProperties(String propertyFileName) + protected void loadProperties(String propertyFileName) { final File propFile = new File(propertyFileName); try { @@ -782,19 +79,4 @@ public class HikariConfig implements HikariConfigMBean throw new RuntimeException("Error loading properties file", io); } } - - 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); - } - } - } - } } diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/HikariConfig.java b/hikaricp/src/main/java/com/zaxxer/hikari/HikariConfig.java index 05395307..e78a8f93 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -20,102 +20,18 @@ 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.sql.Connection; -import java.sql.SQLException; import java.util.Properties; -import java.util.Set; -import java.util.TreeSet; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import javax.sql.DataSource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.zaxxer.hikari.metrics.CodaHaleShim; -import com.zaxxer.hikari.proxy.JavassistProxyFactory; -import com.zaxxer.hikari.util.PoolUtilities; import com.zaxxer.hikari.util.PropertyBeanSetter; -public class HikariConfig implements HikariConfigMBean +public class HikariConfig extends AbstractHikariConfig { - 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; - private static boolean unitTest; - - // 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 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 IConnectionCustomizer customizer; - private ThreadFactory threadFactory; - private Object metricRegistry; - - static { - JavassistProxyFactory.initialize(); - CodaHaleShim.initialize(); - } - /** * Default constructor */ public HikariConfig() { - dataSourceProperties = new Properties(); - - connectionTimeout = CONNECTION_TIMEOUT; - idleTimeout = IDLE_TIMEOUT; - isAutoCommit = true; - isInitializationFailFast = true; - minIdle = -1; - maxPoolSize = 10; - maxLifetime = MAX_LIFETIME; - customizer = new IConnectionCustomizer() { - @Override - public void customize(Connection connection) throws SQLException - { - } - }; - - String systemProp = System.getProperty("hikaricp.configurationFile"); - if ( systemProp != null) { - loadProperties(systemProp); - } + super(); } /** @@ -125,8 +41,7 @@ public class HikariConfig implements HikariConfigMBean */ public HikariConfig(Properties properties) { - this(); - PropertyBeanSetter.setTargetFromProperties(this, properties); + super(properties); } /** @@ -138,628 +53,10 @@ public class HikariConfig implements HikariConfigMBean */ 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 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 Connection.isValid() 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) - { - 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 && !metricRegistry.getClass().getName().contains("MetricRegistry")) { - throw new IllegalArgumentException("Class must be an instance of com.codahale.metrics.MetricRegistry"); - } - this.metricRegistry = metricRegistry; - } - - public boolean isReadOnly() - { - return isReadOnly; + super(propertyFileName); } - 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 Connection class, eg. - * TRANSACTION_REPEATABLE_READ. - * - * @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) { - PoolUtilities.getTransactionIsolation(transactionIsolationName); - } - - if (poolName == null) { - poolName = "HikariPool-" + poolNumber++; - } - - if (LOGGER.isDebugEnabled() || unitTest) { - 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."); - } - - 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(120)) { - logger.warn("maxLifetime is less than 120000ms, using default {}ms.", MAX_LIFETIME); - maxLifetime = MAX_LIFETIME; - } - - if (idleTimeout != 0 && idleTimeout < TimeUnit.SECONDS.toMillis(30)) { - logger.warn("idleTimeout is less than 30000ms, 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(10) && !unitTest) { - logger.warn("leakDetectionThreshold is less than 10000ms, disabling leak detection."); - leakDetectionThreshold = 0; - } - } - - private void logConfiguration() - { - LOGGER.debug("HikariCP pool {} configuration:", poolName); - final Set propertyNames = new TreeSet(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", ""); - value = dsProps; - } - value = (prop.contains("password") ? "" : value); - LOGGER.debug((prop + "................................................").substring(0, 32) + (value != null ? value : "")); - } - catch (Exception e) { - continue; - } - } - } - - private void loadProperties(String propertyFileName) + protected void loadProperties(String propertyFileName) { final File propFile = new File(propertyFileName); try (final InputStream is = propFile.isFile() ? new FileInputStream(propFile) : this.getClass().getResourceAsStream(propertyFileName)) { @@ -776,19 +73,4 @@ public class HikariConfig implements HikariConfigMBean throw new RuntimeException("Error loading properties file", io); } } - - 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); - } - } - } - } }