diff --git a/README.md b/README.md index d928d458..8b7c8e84 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,7 @@ We recommended using ``dataSourceClassName`` instead of ``driverClassName``/``jd | Database | Driver | *DataSource* class | |:---------------- |:------------ |:-------------------| | Apache Derby | Derby | org.apache.derby.jdbc.ClientDataSource | +| IBM DB2 | DB2 | com.ibm.db2.jcc.DB2SimpleDataSource | | H2 | H2 | org.h2.jdbcx.JdbcDataSource | | HSQLDB | HSQLDB | org.hsqldb.jdbc.JDBCDataSource | | MariaDB & MySQL | MariaDB | org.mariadb.jdbc.MySQLDataSource | diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index 74930aad..3893bf2e 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -65,6 +65,7 @@ public class HikariConfig implements HikariConfigMBean private String dataSourceJndiName; private String driverClassName; private String jdbcUrl; + private String metricsTrackerClassName; private String password; private String poolName; private String transactionIsolationName; @@ -102,6 +103,7 @@ public class HikariConfig implements HikariConfigMBean maxLifetime = MAX_LIFETIME; isRecordMetrics = false; transactionIsolation = -1; + metricsTrackerClassName = "com.zaxxer.hikari.metrics.CodaHaleMetricsTracker"; customizer = new IConnectionCustomizer() { @Override public void customize(Connection connection) throws SQLException @@ -521,6 +523,28 @@ public class HikariConfig implements HikariConfigMBean this.maxPoolSize = maxPoolSize; } + /** + * Get the name of the class that implements the IMetricsTracker interface to + * be used for metrics tracking. + * + * @return the name of the class that implements the IMetricsTracker interface + */ + public String getMetricsTrackerClassName() + { + return metricsTrackerClassName; + } + + /** + * Set the name of the class that implements the IMetricsTracker interface to + * be used for metrics tracking. + * + * @param className the name of the class that implements the IMetricsTracker interface + */ + public void setMetricsTrackerClassName(String className) + { + this.metricsTrackerClassName = className; + } + /** {@inheritDoc} */ @Override public int getMinimumIdle() diff --git a/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java b/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java index f0bcc75c..9028de1b 100644 --- a/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java +++ b/src/main/java/com/zaxxer/hikari/metrics/CodaHaleMetricsTracker.java @@ -46,7 +46,7 @@ public final class CodaHaleMetricsTracker extends MetricsTracker connectionUsage.update(usageMilleseconds); } - public static final class Context extends MetricsTracker.Context + public static final class Context extends MetricsContext { Timer.Context innerContext; diff --git a/src/main/java/com/zaxxer/hikari/metrics/IMetricsTracker.java b/src/main/java/com/zaxxer/hikari/metrics/IMetricsTracker.java new file mode 100644 index 00000000..958cadf5 --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/metrics/IMetricsTracker.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Brett Wooldridge + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.zaxxer.hikari.metrics; + +/** + * + * @author Brett Wooldridge + */ +public interface IMetricsTracker +{ + /** + * This method is called when a connection request starts. The {@#MetricsContext.stop()} + * method will be called at the completion of the connection request, whether or not an + * exception occurred. + * + * @param startTime the timestamp of the start time as returned by System.currentTimeMillis() + * @return an instance of MetricsContext + */ + public MetricsContext recordConnectionRequest(long startTime); + + /** + * This method is called when a Connection is closed, with the total time in milliseconds + * that the Connection was out of the pool. + * + * @param usageMilleseconds the Connection usage time in milliseconds + */ + public void recordConnectionUsage(long usageMilleseconds); + + /** + * A base instance of a MetricsContext. Classes extending this class should exhibit the + * behavior of "starting" a timer upon contruction, and "stopping" the timer when the + * {@#stop()} method is called. + * + * @author Brett Wooldridge + */ + public static class MetricsContext + { + public void stop() + { + // do nothing + } + } +} \ No newline at end of file diff --git a/src/main/java/com/zaxxer/hikari/metrics/MetricsFactory.java b/src/main/java/com/zaxxer/hikari/metrics/MetricsFactory.java new file mode 100644 index 00000000..56943782 --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/metrics/MetricsFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Brett Wooldridge + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.zaxxer.hikari.metrics; + +import com.zaxxer.hikari.util.PoolUtilities; + +/** + * + * @author Brett Wooldridge + */ +public final class MetricsFactory +{ + private MetricsFactory() + { + // private contructor + } + + public static final IMetricsTracker createMetricsTracker(String metricsClassName, String poolName) + { + return PoolUtilities.createInstance(metricsClassName, IMetricsTracker.class, poolName); + } +} diff --git a/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java b/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java index f07ab03a..6a656780 100644 --- a/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java +++ b/src/main/java/com/zaxxer/hikari/metrics/MetricsTracker.java @@ -22,19 +22,19 @@ package com.zaxxer.hikari.metrics; * * @author Brett Wooldridge */ -public class MetricsTracker +public class MetricsTracker implements IMetricsTracker { - public static final Context NO_CONTEXT = new Context(); + public static final MetricsContext NO_CONTEXT = new MetricsContext(); - public static class Context + public MetricsTracker() { - public void stop() - { - // do nothing - } } - public Context recordConnectionRequest(long start) + public MetricsTracker(String poolName) + { + } + + public MetricsContext recordConnectionRequest(long requestTime) { return NO_CONTEXT; } diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index c49733e9..1fd05bee 100644 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -16,6 +16,14 @@ package com.zaxxer.hikari.pool; +import static com.zaxxer.hikari.util.PoolUtilities.IS_JAVA7; +import static com.zaxxer.hikari.util.PoolUtilities.createInstance; +import static com.zaxxer.hikari.util.PoolUtilities.createThreadPoolExecutor; +import static com.zaxxer.hikari.util.PoolUtilities.elapsedTimeMs; +import static com.zaxxer.hikari.util.PoolUtilities.executeSqlAutoCommit; +import static com.zaxxer.hikari.util.PoolUtilities.quietlyCloseConnection; +import static com.zaxxer.hikari.util.PoolUtilities.quietlySleep; + import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; @@ -34,9 +42,10 @@ import org.slf4j.LoggerFactory; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.IConnectionCustomizer; -import com.zaxxer.hikari.metrics.CodaHaleMetricsTracker; +import com.zaxxer.hikari.metrics.IMetricsTracker; +import com.zaxxer.hikari.metrics.IMetricsTracker.MetricsContext; +import com.zaxxer.hikari.metrics.MetricsFactory; import com.zaxxer.hikari.metrics.MetricsTracker; -import com.zaxxer.hikari.metrics.MetricsTracker.Context; import com.zaxxer.hikari.proxy.IHikariConnectionProxy; import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.util.ConcurrentBag; @@ -44,14 +53,6 @@ import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; import com.zaxxer.hikari.util.DriverDataSource; import com.zaxxer.hikari.util.PropertyBeanSetter; -import static com.zaxxer.hikari.util.PoolUtilities.elapsedTimeMs; -import static com.zaxxer.hikari.util.PoolUtilities.createInstance; -import static com.zaxxer.hikari.util.PoolUtilities.createThreadPoolExecutor; -import static com.zaxxer.hikari.util.PoolUtilities.executeSqlAutoCommit; -import static com.zaxxer.hikari.util.PoolUtilities.quietlySleep; -import static com.zaxxer.hikari.util.PoolUtilities.quietlyCloseConnection; -import static com.zaxxer.hikari.util.PoolUtilities.IS_JAVA7; - /** * This is the primary connection pool class that provides the basic * pooling behavior for HikariCP. @@ -68,7 +69,11 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private final HikariConfig configuration; private final ConcurrentBag connectionBag; private final ThreadPoolExecutor addConnectionExecutor; - private final MetricsTracker metricsTracker; + private final IMetricsTracker metricsTracker; + + private final AtomicReference lastConnectionFailure; + private final AtomicInteger totalConnections; + private final Timer houseKeepingTimer; private final boolean isAutoCommit; private final boolean isIsolateInternalQueries; @@ -77,9 +82,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private final boolean isRegisteredMbeans; private final boolean isJdbc4ConnectionTest; private final long leakDetectionThreshold; - private final AtomicReference lastConnectionFailure; - private final AtomicInteger totalConnections; - private final Timer houseKeepingTimer; private final String catalog; private final String username; private final String password; @@ -127,7 +129,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener this.leakDetectionThreshold = configuration.getLeakDetectionThreshold(); this.transactionIsolation = configuration.getTransactionIsolation(); this.isRecordMetrics = configuration.isRecordMetrics(); - this.metricsTracker = (isRecordMetrics ? new CodaHaleMetricsTracker(configuration.getPoolName()) : new MetricsTracker()); + this.metricsTracker = MetricsFactory.createMetricsTracker((isRecordMetrics ? configuration.getMetricsTrackerClassName() : "com.zaxxer.hikari.metrics.MetricsTracker"), configuration.getPoolName()); this.dataSource = initializeDataSource(); @@ -154,7 +156,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener public Connection getConnection() throws SQLException { final long start = System.currentTimeMillis(); - final Context context = (isRecordMetrics ? metricsTracker.recordConnectionRequest(start) : MetricsTracker.NO_CONTEXT); + final MetricsContext context = (isRecordMetrics ? metricsTracker.recordConnectionRequest(start) : MetricsTracker.NO_CONTEXT); long timeout = connectionTimeout; try @@ -433,7 +435,8 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { try { - timeoutMs = Math.max(1000L, timeoutMs); + final boolean timeoutEnabled = (configuration.getConnectionTimeout() != Integer.MAX_VALUE); + timeoutMs = timeoutEnabled ? Math.max(1000L, timeoutMs) : 0; if (isJdbc4ConnectionTest) { @@ -443,11 +446,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener Statement statement = connection.createStatement(); try { - if (configuration.getConnectionTimeout() < Integer.MAX_VALUE) - { - statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); - } + statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); statement.executeQuery(configuration.getConnectionTestQuery()); + return true; } finally { @@ -457,8 +458,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener connection.rollback(); } } - - return true; } catch (SQLException e) { @@ -540,16 +539,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener String dsClassName = configuration.getDataSourceClassName(); if (configuration.getDataSource() == null && dsClassName != null) { - try - { - DataSource dataSource = createInstance(dsClassName, DataSource.class); - PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties()); - return dataSource; - } - catch (Exception e) - { - throw new RuntimeException("Could not create datasource instance: " + dsClassName, e); - } + DataSource dataSource = createInstance(dsClassName, DataSource.class); + PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties()); + return dataSource; } else if (configuration.getJdbcUrl() != null) { @@ -563,14 +555,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { if (configuration.getConnectionCustomizerClassName() != null) { - try - { - return createInstance(configuration.getConnectionCustomizerClassName(), IConnectionCustomizer.class); - } - catch (Exception e) - { - LOGGER.error("Connection customizer could not be created", e); - } + return createInstance(configuration.getConnectionCustomizerClassName(), IConnectionCustomizer.class); } return configuration.getConnectionCustomizer(); diff --git a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java index f4e3988c..b0e594ee 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java @@ -58,7 +58,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy private boolean forceClose; private boolean isAutoCommitDirty; private boolean isCatalogDirty; - private volatile boolean isClosed; + private boolean isClosed; private boolean isReadOnlyDirty; private boolean isTransactionIsolationDirty; private volatile long lastAccess; diff --git a/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java b/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java index 41ef42ee..14ba21d5 100644 --- a/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java +++ b/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java @@ -1,5 +1,6 @@ package com.zaxxer.hikari.util; +import java.lang.reflect.Constructor; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; @@ -90,7 +91,7 @@ public final class PoolUtilities } @SuppressWarnings("unchecked") - public static T createInstance(String className, Class clazz) + public static T createInstance(String className, Class clazz, Object...args) { if (className == null) { @@ -100,6 +101,19 @@ public final class PoolUtilities try { Class loaded = PoolUtilities.class.getClassLoader().loadClass(className); + + Class[] argClasses = new Class[args.length]; + for (int i = 0; i < args.length; i++) + { + argClasses[i] = args[i].getClass(); + } + + if (args.length > 0) + { + Constructor constructor = loaded.getConstructor(argClasses); + return (T) constructor.newInstance(args); + } + return (T) loaded.newInstance(); } catch (Exception e)