summaryrefslogtreecommitdiff
path: root/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/com/zaxxer/hikari/db/BasicPoolTest.java149
-rwxr-xr-xsrc/test/java/com/zaxxer/hikari/metrics/dropwizard/CodaHaleMetricsTrackerTest.java39
-rw-r--r--src/test/java/com/zaxxer/hikari/metrics/prometheus/HikariCPCollectorTest.java125
-rw-r--r--src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerTest.java78
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java162
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java47
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/StubConnection.java486
-rwxr-xr-xsrc/test/java/com/zaxxer/hikari/mocks/StubDataSource.java149
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/StubDriver.java96
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java671
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java1292
-rw-r--r--src/test/java/com/zaxxer/hikari/mocks/StubStatement.java387
-rw-r--r--src/test/java/com/zaxxer/hikari/osgi/OSGiBundleTest.java91
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/ConcurrentCloseConnectionTest.java76
-rwxr-xr-xsrc/test/java/com/zaxxer/hikari/pool/ConnectionPoolSizeVsThreadsTest.java198
-rwxr-xr-xsrc/test/java/com/zaxxer/hikari/pool/ConnectionRaceConditionTest.java106
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/ConnectionStateTest.java170
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/ExceptionTest.java119
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/HouseKeeperCleanupTest.java84
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/IsolationTest.java74
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/JdbcDriverTest.java86
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/MiscTest.java134
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/PostgresTest.java231
-rwxr-xr-xsrc/test/java/com/zaxxer/hikari/pool/RampUpDown.java75
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/ShutdownTest.java356
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/StatementTest.java119
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestConcurrentBag.java117
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestConnectionCloseBlocking.java98
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestConnectionTimeoutRetry.java277
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestConnections.java621
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestElf.java177
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestHibernate.java57
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestJNDI.java146
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestMBean.java44
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestMetrics.java303
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestPropertySetter.java111
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestProxies.java324
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/TestValidation.java253
-rw-r--r--src/test/java/com/zaxxer/hikari/pool/UnwrapTest.java90
-rw-r--r--src/test/java/com/zaxxer/hikari/util/ClockSourceTest.java60
-rw-r--r--src/test/java/com/zaxxer/hikari/util/TestFastList.java172
-rw-r--r--src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java328
42 files changed, 8778 insertions, 0 deletions
diff --git a/src/test/java/com/zaxxer/hikari/db/BasicPoolTest.java b/src/test/java/com/zaxxer/hikari/db/BasicPoolTest.java
new file mode 100644
index 0000000..c9040b0
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/db/BasicPoolTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 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.db;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.pool.HikariPool;
+
+/**
+ * @author brettw
+ *
+ */
+public class BasicPoolTest
+{
+ @Before
+ public void setup() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(2);
+ config.setConnectionTestQuery("SELECT 1");
+ config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
+ config.addDataSourceProperty("url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
+
+ try (HikariDataSource ds = new HikariDataSource(config);
+ Connection conn = ds.getConnection();
+ Statement stmt = conn.createStatement()) {
+ stmt.executeUpdate("DROP TABLE IF EXISTS basic_pool_test");
+ stmt.executeUpdate("CREATE TABLE basic_pool_test ("
+ + "id INTEGER NOT NULL IDENTITY PRIMARY KEY, "
+ + "timestamp TIMESTAMP, "
+ + "string VARCHAR(128), "
+ + "string_from_number NUMERIC "
+ + ")");
+ }
+ }
+
+ @Test
+ public void testIdleTimeout() throws InterruptedException, SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(10);
+ config.setConnectionTestQuery("SELECT 1");
+ config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
+ config.addDataSourceProperty("url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "1000");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ System.clearProperty("com.zaxxer.hikari.housekeeping.periodMs");
+
+ SECONDS.sleep(1);
+
+ HikariPool pool = getPool(ds);
+
+ ds.setIdleTimeout(3000);
+
+ assertEquals("Total connections not as expected", 5, pool.getTotalConnections());
+ assertEquals("Idle connections not as expected", 5, pool.getIdleConnections());
+
+ try (Connection connection = ds.getConnection()) {
+ Assert.assertNotNull(connection);
+
+ MILLISECONDS.sleep(1500);
+
+ assertEquals("Second total connections not as expected", 6, pool.getTotalConnections());
+ assertEquals("Second idle connections not as expected", 5, pool.getIdleConnections());
+ }
+
+ assertEquals("Idle connections not as expected", 6, pool.getIdleConnections());
+
+ SECONDS.sleep(2);
+
+ assertEquals("Third total connections not as expected", 5, pool.getTotalConnections());
+ assertEquals("Third idle connections not as expected", 5, pool.getIdleConnections());
+ }
+ }
+
+ @Test
+ public void testIdleTimeout2() throws InterruptedException, SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMaximumPoolSize(50);
+ config.setConnectionTestQuery("SELECT 1");
+ config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
+ config.addDataSourceProperty("url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "1000");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ System.clearProperty("com.zaxxer.hikari.housekeeping.periodMs");
+
+ SECONDS.sleep(1);
+
+ HikariPool pool = getPool(ds);
+
+ ds.setIdleTimeout(3000);
+
+ assertEquals("Total connections not as expected", 50, pool.getTotalConnections());
+ assertEquals("Idle connections not as expected", 50, pool.getIdleConnections());
+
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+
+ MILLISECONDS.sleep(1500);
+
+ assertEquals("Second total connections not as expected", 50, pool.getTotalConnections());
+ assertEquals("Second idle connections not as expected", 49, pool.getIdleConnections());
+ }
+
+ assertEquals("Idle connections not as expected", 50, pool.getIdleConnections());
+
+ SECONDS.sleep(3);
+
+ assertEquals("Third total connections not as expected", 50, pool.getTotalConnections());
+ assertEquals("Third idle connections not as expected", 50, pool.getIdleConnections());
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/metrics/dropwizard/CodaHaleMetricsTrackerTest.java b/src/test/java/com/zaxxer/hikari/metrics/dropwizard/CodaHaleMetricsTrackerTest.java
new file mode 100755
index 0000000..8e2164c
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/metrics/dropwizard/CodaHaleMetricsTrackerTest.java
@@ -0,0 +1,39 @@
+package com.zaxxer.hikari.metrics.dropwizard;
+
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.codahale.metrics.MetricRegistry;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CodaHaleMetricsTrackerTest {
+
+ @Mock
+ public MetricRegistry mockMetricRegistry;
+
+ private CodaHaleMetricsTracker testee;
+
+ @Before
+ public void setup(){
+ testee = new CodaHaleMetricsTracker("mypool", null, mockMetricRegistry);
+ }
+
+ @Test
+ public void close() throws Exception {
+ testee.close();
+
+ verify(mockMetricRegistry).remove("mypool.pool.Wait");
+ verify(mockMetricRegistry).remove("mypool.pool.Usage");
+ verify(mockMetricRegistry).remove("mypool.pool.ConnectionCreation");
+ verify(mockMetricRegistry).remove("mypool.pool.ConnectionTimeoutRate");
+ verify(mockMetricRegistry).remove("mypool.pool.TotalConnections");
+ verify(mockMetricRegistry).remove("mypool.pool.IdleConnections");
+ verify(mockMetricRegistry).remove("mypool.pool.ActiveConnections");
+ verify(mockMetricRegistry).remove("mypool.pool.PendingConnections");
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/metrics/prometheus/HikariCPCollectorTest.java b/src/test/java/com/zaxxer/hikari/metrics/prometheus/HikariCPCollectorTest.java
new file mode 100644
index 0000000..46b2e74
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/metrics/prometheus/HikariCPCollectorTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.metrics.prometheus;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.sql.Connection;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubConnection;
+
+import io.prometheus.client.CollectorRegistry;
+
+public class HikariCPCollectorTest {
+ @Test
+ public void noConnection() throws Exception {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory());
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ StubConnection.slowCreate = true;
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ assertThat(getValue("hikaricp_active_connections", "noConnection"), is(0.0));
+ assertThat(getValue("hikaricp_idle_connections", "noConnection"), is(0.0));
+ assertThat(getValue("hikaricp_pending_threads", "noConnection"), is(0.0));
+ assertThat(getValue("hikaricp_connections", "noConnection"), is(0.0));
+ }
+ finally {
+ StubConnection.slowCreate = false;
+ }
+ }
+
+ @Test
+ public void noConnectionWithoutPoolName() throws Exception {
+ HikariConfig config = new HikariConfig();
+ config.setMinimumIdle(0);
+ config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory());
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ StubConnection.slowCreate = true;
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ assertThat(getValue("hikaricp_active_connections", "HikariPool-1"), is(0.0));
+ assertThat(getValue("hikaricp_idle_connections", "HikariPool-1"), is(0.0));
+ assertThat(getValue("hikaricp_pending_threads", "HikariPool-1"), is(0.0));
+ assertThat(getValue("hikaricp_connections", "HikariPool-1"), is(0.0));
+ }
+ finally {
+ StubConnection.slowCreate = false;
+ }
+ }
+
+ @Test
+ public void connection1() throws Exception {
+ HikariConfig config = newHikariConfig();
+ config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory());
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.setMaximumPoolSize(1);
+
+ StubConnection.slowCreate = true;
+ try (HikariDataSource ds = new HikariDataSource(config);
+ Connection connection1 = ds.getConnection()) {
+
+ quietlySleep(1000);
+
+ assertThat(getValue("hikaricp_active_connections", "connection1"), is(1.0));
+ assertThat(getValue("hikaricp_idle_connections", "connection1"), is(0.0));
+ assertThat(getValue("hikaricp_pending_threads", "connection1"), is(0.0));
+ assertThat(getValue("hikaricp_connections", "connection1"), is(1.0));
+ }
+ finally {
+ StubConnection.slowCreate = false;
+ }
+ }
+
+ @Test
+ public void connectionClosed() throws Exception {
+ HikariConfig config = newHikariConfig();
+ config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory());
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.setMaximumPoolSize(1);
+
+ StubConnection.slowCreate = true;
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ try (Connection connection1 = ds.getConnection()) {
+ // close immediately
+ }
+
+ assertThat(getValue("hikaricp_active_connections", "connectionClosed"), is(0.0));
+ assertThat(getValue("hikaricp_idle_connections", "connectionClosed"), is(1.0));
+ assertThat(getValue("hikaricp_pending_threads", "connectionClosed"), is(0.0));
+ assertThat(getValue("hikaricp_connections", "connectionClosed"), is(1.0));
+ }
+ finally {
+ StubConnection.slowCreate = false;
+ }
+ }
+
+ private double getValue(String name, String poolName) {
+ String[] labelNames = {"pool"};
+ String[] labelValues = {poolName};
+ return CollectorRegistry.defaultRegistry.getSampleValue(name, labelNames, labelValues);
+ }
+
+}
diff --git a/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerTest.java b/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerTest.java
new file mode 100644
index 0000000..a7b0b03
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.metrics.prometheus;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.SQLTransientConnectionException;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import io.prometheus.client.CollectorRegistry;
+
+public class PrometheusMetricsTrackerTest {
+ @Test
+ public void recordConnectionTimeout() throws Exception {
+ HikariConfig config = newHikariConfig();
+ config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory());
+ config.setJdbcUrl("jdbc:h2:mem:");
+ config.setMaximumPoolSize(2);
+ config.setConnectionTimeout(250);
+
+ String[] labelNames = {"pool"};
+ String[] labelValues = {config.getPoolName()};
+
+ try (HikariDataSource hikariDataSource = new HikariDataSource(config)) {
+ try (Connection connection1 = hikariDataSource.getConnection();
+ Connection connection2 = hikariDataSource.getConnection()) {
+ try (Connection connection3 = hikariDataSource.getConnection()) {
+ }
+ catch (SQLTransientConnectionException ignored) {
+ }
+ }
+
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue(
+ "hikaricp_connection_timeout_count",
+ labelNames,
+ labelValues), is(1.0));
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue(
+ "hikaricp_connection_acquired_nanos_count",
+ labelNames,
+ labelValues), is(equalTo(2.0)));
+ assertTrue(CollectorRegistry.defaultRegistry.getSampleValue(
+ "hikaricp_connection_acquired_nanos_sum",
+ labelNames,
+ labelValues) > 0.0);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue(
+ "hikaricp_connection_usage_millis_count",
+ labelNames,
+ labelValues), is(equalTo(2.0)));
+ assertTrue(CollectorRegistry.defaultRegistry.getSampleValue(
+ "hikaricp_connection_usage_millis_sum",
+ labelNames,
+ labelValues) > 0.0);
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java b/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java
new file mode 100644
index 0000000..558f9fb
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/MockDataSource.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.PrintWriter;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class MockDataSource implements DataSource
+{
+ @Override
+ public Connection getConnection() throws SQLException
+ {
+ return createMockConnection();
+ }
+
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException
+ {
+ return getConnection();
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException
+ {
+ return null;
+ }
+
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException
+ {
+ }
+
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException
+ {
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException
+ {
+ return 0;
+ }
+
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException
+ {
+ return null;
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException
+ {
+ return null;
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException
+ {
+ return false;
+ }
+
+ public static Connection createMockConnection() throws SQLException {
+ // Setup mock connection
+ final Connection mockConnection = mock(Connection.class);
+
+ // Autocommit is always true by default
+ when(mockConnection.getAutoCommit()).thenReturn(true);
+
+ // Handle Connection.createStatement()
+ Statement statement = mock(Statement.class);
+ when(mockConnection.createStatement()).thenReturn(statement);
+ when(mockConnection.createStatement(anyInt(), anyInt())).thenReturn(statement);
+ when(mockConnection.createStatement(anyInt(), anyInt(), anyInt())).thenReturn(statement);
+ when(mockConnection.isValid(anyInt())).thenReturn(true);
+
+ // Handle Connection.prepareStatement()
+ PreparedStatement mockPreparedStatement = mock(PreparedStatement.class);
+ when(mockConnection.prepareStatement(anyString())).thenReturn(mockPreparedStatement);
+ when(mockConnection.prepareStatement(anyString(), anyInt())).thenReturn(mockPreparedStatement);
+ when(mockConnection.prepareStatement(anyString(), any(int[].class))).thenReturn(mockPreparedStatement);
+ when(mockConnection.prepareStatement(anyString(), any(String[].class))).thenReturn(mockPreparedStatement);
+ when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt())).thenReturn(mockPreparedStatement);
+ when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(mockPreparedStatement);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable
+ {
+ return null;
+ }
+ }).doNothing().when(mockPreparedStatement).setInt(anyInt(), anyInt());
+
+ ResultSet mockResultSet = mock(ResultSet.class);
+ when(mockPreparedStatement.executeQuery()).thenReturn(mockResultSet);
+ when(mockResultSet.getString(anyInt())).thenReturn("aString");
+ when(mockResultSet.next()).thenReturn(true);
+
+ // Handle Connection.prepareCall()
+ CallableStatement mockCallableStatement = mock(CallableStatement.class);
+ when(mockConnection.prepareCall(anyString())).thenReturn(mockCallableStatement);
+ when(mockConnection.prepareCall(anyString(), anyInt(), anyInt())).thenReturn(mockCallableStatement);
+ when(mockConnection.prepareCall(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(mockCallableStatement);
+
+ // Handle Connection.close()
+// doAnswer(new Answer<Void>() {
+// public Void answer(InvocationOnMock invocation) throws Throwable {
+// return null;
+// }
+// }).doThrow(new SQLException("Connection is already closed")).when(mockConnection).close();
+
+ // Handle Connection.commit()
+// doAnswer(new Answer<Void>() {
+// public Void answer(InvocationOnMock invocation) throws Throwable {
+// return null;
+// }
+// }).doThrow(new SQLException("Transaction already commited")).when(mockConnection).commit();
+
+ // Handle Connection.rollback()
+// doAnswer(new Answer<Void>() {
+// public Void answer(InvocationOnMock invocation) throws Throwable {
+// return null;
+// }
+// }).doThrow(new SQLException("Transaction already rolledback")).when(mockConnection).rollback();
+
+ return mockConnection;
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java
new file mode 100644
index 0000000..f88705e
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubBaseConnection.java
@@ -0,0 +1,47 @@
+/*
+ * 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.mocks;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public abstract class StubBaseConnection implements Connection
+{
+ public volatile boolean throwException;
+
+ /** {@inheritDoc} */
+ @Override
+ public Statement createStatement() throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubStatement(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PreparedStatement prepareStatement(String sql) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubPreparedStatement(this);
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java
new file mode 100644
index 0000000..f96624b
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.zaxxer.hikari.util.UtilityElf;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class StubConnection extends StubBaseConnection implements Connection
+{
+ public static final AtomicInteger count = new AtomicInteger();
+ public static volatile boolean slowCreate;
+ public static volatile boolean oldDriver;
+
+ private static long foo;
+ private boolean autoCommit;
+ private int isolation = Connection.TRANSACTION_READ_COMMITTED;
+ private String catalog;
+
+ static {
+ foo = System.currentTimeMillis();
+ }
+
+ public StubConnection() {
+ count.incrementAndGet();
+ if (slowCreate) {
+ UtilityElf.quietlySleep(1000);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+
+ if (iface.isInstance(this)) {
+ return (T) this;
+ }
+
+ throw new SQLException("Wrapped connection is not an instance of " + iface);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CallableStatement prepareCall(String sql) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String nativeSQL(String sql) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAutoCommit(boolean autoCommit) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ this.autoCommit = autoCommit;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getAutoCommit() throws SQLException
+ {
+ return autoCommit;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void commit() throws SQLException
+ {
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void rollback() throws SQLException
+ {
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void close() throws SQLException
+ {
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isClosed() throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DatabaseMetaData getMetaData() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setReadOnly(boolean readOnly) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isReadOnly() throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCatalog(String catalog) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ this.catalog = catalog;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getCatalog() throws SQLException
+ {
+ return catalog;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTransactionIsolation(int level) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ this.isolation = level;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getTransactionIsolation() throws SQLException
+ {
+ return isolation;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLWarning getWarnings() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearWarnings() throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubPreparedStatement(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Map<String, Class<?>> getTypeMap() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTypeMap(Map<String, Class<?>> map) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setHoldability(int holdability) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getHoldability() throws SQLException
+ {
+ return (int) foo;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Savepoint setSavepoint() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Savepoint setSavepoint(String name) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void rollback(Savepoint savepoint) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void releaseSavepoint(Savepoint savepoint) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubPreparedStatement(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubPreparedStatement(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubPreparedStatement(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return new StubPreparedStatement(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Clob createClob() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Blob createBlob() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public NClob createNClob() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLXML createSQLXML() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isValid(int timeout) throws SQLException
+ {
+ if (throwException) {
+ throw new SQLException();
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClientInfo(String name, String value) throws SQLClientInfoException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClientInfo(Properties properties) throws SQLClientInfoException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getClientInfo(String name) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Properties getClientInfo() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Array createArrayOf(String typeName, Object[] elements) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Struct createStruct(String typeName, Object[] attributes) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public void setSchema(String schema) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ public String getSchema() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public void abort(Executor executor) throws SQLException
+ {
+ throw new SQLException("Intentional exception during abort");
+ }
+
+ /** {@inheritDoc} */
+ public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ public int getNetworkTimeout() throws SQLException
+ {
+ if (oldDriver) {
+ throw new AbstractMethodError();
+ }
+
+ return 0;
+ }
+
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java b/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java
new file mode 100755
index 0000000..c7da189
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import com.zaxxer.hikari.util.UtilityElf;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class StubDataSource implements DataSource
+{
+ private String user;
+ private String password;
+ private PrintWriter logWriter;
+ private SQLException throwException;
+ private long connectionAcquistionTime = 0;
+ private int loginTimeout;
+
+ public String getUser()
+ {
+ return user;
+ }
+
+ public void setUser(String user)
+ {
+ this.user = user;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public void setURL(String url)
+ {
+ // we don't care
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PrintWriter getLogWriter() throws SQLException
+ {
+ return logWriter;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException
+ {
+ this.logWriter = out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException
+ {
+ this.loginTimeout = seconds;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getLoginTimeout() throws SQLException
+ {
+ return loginTimeout;
+ }
+
+ /** {@inheritDoc} */
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException
+ {
+ if (iface.isInstance(this)) {
+ return (T) this;
+ }
+
+ throw new SQLException("Wrapped DataSource is not an instance of " + iface);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Connection getConnection() throws SQLException
+ {
+ if (throwException != null) {
+ throw throwException;
+ }
+ if (connectionAcquistionTime > 0) {
+ UtilityElf.quietlySleep(connectionAcquistionTime);
+ }
+
+ return new StubConnection();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException
+ {
+ return new StubConnection();
+ }
+
+ public void setThrowException(SQLException e)
+ {
+ this.throwException = e;
+ }
+
+ public void setConnectionAcquistionTime(long connectionAcquisitionTime) {
+ this.connectionAcquistionTime = connectionAcquisitionTime;
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java b/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java
new file mode 100644
index 0000000..eff7292
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubDriver.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class StubDriver implements Driver
+{
+ private static final Driver driver;
+
+ static
+ {
+ driver = new StubDriver();
+ try
+ {
+ DriverManager.registerDriver(driver);
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Connection connect(String url, Properties info) throws SQLException
+ {
+ return new StubConnection();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean acceptsURL(String url) throws SQLException
+ {
+ return "jdbc:stub".equals(url);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getMajorVersion()
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getMinorVersion()
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean jdbcCompliant()
+ {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException
+ {
+ return null;
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java b/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java
new file mode 100644
index 0000000..5c48f85
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubPreparedStatement.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class StubPreparedStatement extends StubStatement implements PreparedStatement
+{
+ public StubPreparedStatement(Connection connection)
+ {
+ super(connection);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet executeQuery(String sql) throws SQLException
+ {
+ return new StubResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getMaxFieldSize() throws SQLException
+ {
+ throw new SQLException("Simulated disconnection error", "08999");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setMaxFieldSize(int max) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getMaxRows() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setMaxRows(int max) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEscapeProcessing(boolean enable) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getQueryTimeout() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setQueryTimeout(int seconds) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void cancel() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLWarning getWarnings() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearWarnings() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCursorName(String name) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet getResultSet() throws SQLException
+ {
+ return new StubResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getUpdateCount() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getMoreResults() throws SQLException
+ {
+ if (isClosed())
+ {
+ throw new SQLException("Connection is closed");
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFetchDirection(int direction) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getFetchDirection() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFetchSize(int rows) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getFetchSize() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getResultSetConcurrency() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getResultSetType() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addBatch(String sql) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearBatch() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int[] executeBatch() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getMoreResults(int current) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet getGeneratedKeys() throws SQLException
+ {
+ return new StubResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, String[] columnNames) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, int[] columnIndexes) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, String[] columnNames) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getResultSetHoldability() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setPoolable(boolean poolable) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isPoolable() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void closeOnCompletion() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCloseOnCompletion() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException
+ {
+ if (iface.isInstance(this)) {
+ return (T) this;
+ }
+
+ throw new SQLException("Wrapped connection is not an instance of " + iface);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet executeQuery() throws SQLException
+ {
+ return new StubResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNull(int parameterIndex, int sqlType) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBoolean(int parameterIndex, boolean x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setByte(int parameterIndex, byte x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setShort(int parameterIndex, short x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setInt(int parameterIndex, int x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setLong(int parameterIndex, long x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFloat(int parameterIndex, float x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setDouble(int parameterIndex, double x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setString(int parameterIndex, String x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBytes(int parameterIndex, byte[] x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setDate(int parameterIndex, Date x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTime(int parameterIndex, Time x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearParameters() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int parameterIndex, Object x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addBatch() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRef(int parameterIndex, Ref x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBlob(int parameterIndex, Blob x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClob(int parameterIndex, Clob x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setArray(int parameterIndex, Array x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSetMetaData getMetaData() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setURL(int parameterIndex, URL x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ParameterMetaData getParameterMetaData() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowId(int parameterIndex, RowId x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNString(int parameterIndex, String value) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNClob(int parameterIndex, NClob value) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClob(int parameterIndex, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setClob(int parameterIndex, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setNClob(int parameterIndex, Reader reader) throws SQLException
+ {
+ }
+
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java b/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java
new file mode 100644
index 0000000..911daba
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubResultSet.java
@@ -0,0 +1,1292 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class StubResultSet implements ResultSet
+{
+ private int counter;
+ private boolean closed;
+
+ /** {@inheritDoc} */
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException
+ {
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean next() throws SQLException
+ {
+ return (counter > 100000);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void close() throws SQLException
+ {
+ closed = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wasNull() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getString(int columnIndex) throws SQLException
+ {
+ return "aString";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getBoolean(int columnIndex) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public byte getByte(int columnIndex) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public short getShort(int columnIndex) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getInt(int columnIndex) throws SQLException
+ {
+ return ++counter;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long getLong(int columnIndex) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public float getFloat(int columnIndex) throws SQLException
+ {
+ throw new SQLException("Simulated disconnection error", "08999");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDouble(int columnIndex) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public byte[] getBytes(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Date getDate(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Time getTime(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Timestamp getTimestamp(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InputStream getAsciiStream(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InputStream getUnicodeStream(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InputStream getBinaryStream(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getString(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getBoolean(String columnLabel) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public byte getByte(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public short getShort(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getInt(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long getLong(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public float getFloat(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDouble(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public byte[] getBytes(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Date getDate(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Time getTime(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Timestamp getTimestamp(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InputStream getAsciiStream(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InputStream getUnicodeStream(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public InputStream getBinaryStream(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLWarning getWarnings() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearWarnings() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getCursorName() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSetMetaData getMetaData() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Object getObject(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Object getObject(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int findColumn(String columnLabel) throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Reader getCharacterStream(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Reader getCharacterStream(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BigDecimal getBigDecimal(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BigDecimal getBigDecimal(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isBeforeFirst() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isAfterLast() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isFirst() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isLast() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void beforeFirst() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void afterLast() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean first() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean last() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRow() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean absolute(int row) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean relative(int rows) throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean previous() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFetchDirection(int direction) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getFetchDirection() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFetchSize(int rows) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getFetchSize() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getType() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getConcurrency() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean rowUpdated() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean rowInserted() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean rowDeleted() throws SQLException
+ {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNull(int columnIndex) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBoolean(int columnIndex, boolean x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateByte(int columnIndex, byte x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateShort(int columnIndex, short x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateInt(int columnIndex, int x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateLong(int columnIndex, long x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateFloat(int columnIndex, float x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateDouble(int columnIndex, double x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateString(int columnIndex, String x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBytes(int columnIndex, byte[] x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateDate(int columnIndex, Date x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateTime(int columnIndex, Time x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateObject(int columnIndex, Object x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNull(String columnLabel) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBoolean(String columnLabel, boolean x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateByte(String columnLabel, byte x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateShort(String columnLabel, short x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateInt(String columnLabel, int x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateLong(String columnLabel, long x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateFloat(String columnLabel, float x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateDouble(String columnLabel, double x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateString(String columnLabel, String x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBytes(String columnLabel, byte[] x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateDate(String columnLabel, Date x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateTime(String columnLabel, Time x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateObject(String columnLabel, Object x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void insertRow() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateRow() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void deleteRow() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void refreshRow() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void cancelRowUpdates() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void moveToInsertRow() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void moveToCurrentRow() throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Statement getStatement() throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Ref getRef(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Blob getBlob(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Clob getClob(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Array getArray(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Ref getRef(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Blob getBlob(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Clob getClob(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Array getArray(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Date getDate(int columnIndex, Calendar cal) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Date getDate(String columnLabel, Calendar cal) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Time getTime(int columnIndex, Calendar cal) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Time getTime(String columnLabel, Calendar cal) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public URL getURL(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public URL getURL(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateRef(int columnIndex, Ref x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateRef(String columnLabel, Ref x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBlob(int columnIndex, Blob x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBlob(String columnLabel, Blob x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateClob(int columnIndex, Clob x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateClob(String columnLabel, Clob x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateArray(int columnIndex, Array x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateArray(String columnLabel, Array x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RowId getRowId(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RowId getRowId(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateRowId(int columnIndex, RowId x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateRowId(String columnLabel, RowId x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getHoldability() throws SQLException
+ {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isClosed() throws SQLException
+ {
+ return closed;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNString(int columnIndex, String nString) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNString(String columnLabel, String nString) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNClob(int columnIndex, NClob nClob) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNClob(String columnLabel, NClob nClob) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public NClob getNClob(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public NClob getNClob(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLXML getSQLXML(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLXML getSQLXML(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getNString(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getNString(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Reader getNCharacterStream(int columnIndex) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Reader getNCharacterStream(String columnLabel) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateClob(int columnIndex, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateClob(String columnLabel, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateCharacterStream(int columnIndex, Reader x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateClob(int columnIndex, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateClob(String columnLabel, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNClob(int columnIndex, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void updateNClob(String columnLabel, Reader reader) throws SQLException
+ {
+ }
+
+ /** {@inheritDoc} */
+ public <T> T getObject(int columnIndex, Class<T> type) throws SQLException
+ {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public <T> T getObject(String columnLabel, Class<T> type) throws SQLException
+ {
+ return null;
+ }
+
+}
diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java b/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java
new file mode 100644
index 0000000..73fec69
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.mocks;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class StubStatement implements Statement
+{
+ public static volatile boolean oldDriver;
+ private boolean closed;
+ private Connection connection;
+
+ public StubStatement(Connection connection) {
+ this.connection = connection;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException
+ {
+ checkClosed();
+ return (T) this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet executeQuery(String sql) throws SQLException
+ {
+ checkClosed();
+ StubResultSet resultSet = new StubResultSet();
+ return resultSet;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql) throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void close() throws SQLException
+ {
+ closed = true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getMaxFieldSize() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setMaxFieldSize(int max) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getMaxRows() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setMaxRows(int max) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEscapeProcessing(boolean enable) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getQueryTimeout() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setQueryTimeout(int seconds) throws SQLException
+ {
+ if (oldDriver) {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void cancel() throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SQLWarning getWarnings() throws SQLException
+ {
+ checkClosed();
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearWarnings() throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setCursorName(String name) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql) throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet getResultSet() throws SQLException
+ {
+ checkClosed();
+ return new StubResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getUpdateCount() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getMoreResults() throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFetchDirection(int direction) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getFetchDirection() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setFetchSize(int rows) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getFetchSize() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getResultSetConcurrency() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getResultSetType() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addBatch(String sql) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearBatch() throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int[] executeBatch() throws SQLException
+ {
+ checkClosed();
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Connection getConnection() throws SQLException
+ {
+ checkClosed();
+ return connection;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean getMoreResults(int current) throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResultSet getGeneratedKeys() throws SQLException
+ {
+ checkClosed();
+ return new StubResultSet();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int executeUpdate(String sql, String[] columnNames) throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, int[] columnIndexes) throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean execute(String sql, String[] columnNames) throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getResultSetHoldability() throws SQLException
+ {
+ checkClosed();
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isClosed() throws SQLException
+ {
+ return closed;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setPoolable(boolean poolable) throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isPoolable() throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public void closeOnCompletion() throws SQLException
+ {
+ checkClosed();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCloseOnCompletion() throws SQLException
+ {
+ checkClosed();
+ return false;
+ }
+
+ private void checkClosed() throws SQLException
+ {
+ if (closed) {
+ throw new SQLException("Statement is closed");
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/osgi/OSGiBundleTest.java b/src/test/java/com/zaxxer/hikari/osgi/OSGiBundleTest.java
new file mode 100644
index 0000000..761a802
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/osgi/OSGiBundleTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zaxxer.hikari.osgi;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import javax.inject.Inject;
+
+import java.io.File;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.*;
+
+/**
+ * @author lburgazzoli
+ */
+@RunWith(PaxExam.class)
+public class OSGiBundleTest
+{
+ @Inject
+ BundleContext context;
+
+ @Configuration
+ public Option[] config()
+ {
+ return options(
+ systemProperty("org.osgi.framework.storage.clean").value("true"),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("WARN"),
+ mavenBundle("org.slf4j","slf4j-api","1.7.5"),
+ mavenBundle("org.slf4j","slf4j-simple","1.7.5").noStart(),
+ mavenBundle("org.javassist", "javassist", "3.19.0-GA"),
+ new File("target/classes").exists()
+ ? bundle("reference:file:target/classes")
+ : bundle("reference:file:../target/classes"),
+ junitBundles(),
+ cleanCaches()
+ );
+ }
+
+ @Test
+ public void checkInject()
+ {
+ assertNotNull(context);
+ }
+
+ @Test
+ public void checkBundle()
+ {
+ Boolean bundleFound = false;
+ Boolean bundleActive = false;
+
+ Bundle[] bundles = context.getBundles();
+ for(Bundle bundle : bundles)
+ {
+ if(bundle != null)
+ {
+ if(bundle.getSymbolicName().equals("com.zaxxer.HikariCP"))
+ {
+ bundleFound = true;
+ if(bundle.getState() == Bundle.ACTIVE)
+ {
+ bundleActive = true;
+ }
+ }
+ }
+ }
+
+ assertTrue(bundleFound);
+ assertTrue(bundleActive);
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/ConcurrentCloseConnectionTest.java b/src/test/java/com/zaxxer/hikari/pool/ConcurrentCloseConnectionTest.java
new file mode 100644
index 0000000..8820b79
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/ConcurrentCloseConnectionTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+/**
+ * @author Matthew Tambara (matthew.tambara@liferay.com)
+ */
+public class ConcurrentCloseConnectionTest
+{
+ @Test
+ public void testConcurrentClose() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config);
+ final Connection connection = ds.getConnection()) {
+
+ ExecutorService executorService = Executors.newFixedThreadPool(10);
+
+ List<Future<?>> futures = new ArrayList<>();
+
+ for (int i = 0; i < 500; i++) {
+ final PreparedStatement preparedStatement =
+ connection.prepareStatement("");
+
+ futures.add(executorService.submit(new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+ preparedStatement.close();
+
+ return null;
+ }
+
+ }));
+ }
+
+ executorService.shutdown();
+
+ for (Future<?> future : futures) {
+ future.get();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/ConnectionPoolSizeVsThreadsTest.java b/src/test/java/com/zaxxer/hikari/pool/ConnectionPoolSizeVsThreadsTest.java
new file mode 100755
index 0000000..e66ba58
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/ConnectionPoolSizeVsThreadsTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2013, 2017 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.util.ClockSource.currentTime;
+import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubDataSource;
+
+/**
+ * @author Matthew Tambara (matthew.tambara@liferay.com)
+ */
+public class ConnectionPoolSizeVsThreadsTest {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger(ConnectionPoolSizeVsThreadsTest.class);
+
+ public static final int ITERATIONS = 50_000;
+
+ @Test
+ public void testPoolSizeAboutSameSizeAsThreadCount() throws Exception {
+ final int threadCount = 50;
+ final Counts counts = testPoolSize(2 /*minIdle*/,
+ 100 /*maxPoolSize*/,
+ threadCount,
+ 1 /*workTimeMs*/,
+ 0 /*restTimeMs*/,
+ 20 /*connectionAcquisitionTimeMs*/,
+ ITERATIONS,
+ SECONDS.toMillis(2) /*postTestTimeMs*/);
+
+ // maxActive may never make it to threadCount but it shouldn't be any higher
+ assertEquals(threadCount, counts.maxActive, 15 /*delta*/);
+ assertEquals(threadCount, counts.maxTotal, 5 /*delta*/);
+ }
+
+ @Test
+ public void testSlowConnectionTimeBurstyWork() throws Exception {
+ // setup a bursty work load, 50 threads all needing to do around 100 units of work.
+ // Using a more realistic time for connection startup of 250 ms and only 5 seconds worth of work will mean that we end up finishing
+ // all of the work before we actually have setup 50 connections even though we have requested 50 connections
+ final int threadCount = 50;
+ final int workItems = threadCount * 100;
+ final int workTimeMs = 0;
+ final int connectionAcquisitionTimeMs = 250;
+ final Counts counts = testPoolSize(2 /*minIdle*/,
+ 100 /*maxPoolSize*/,
+ threadCount,
+ workTimeMs,
+ 0 /*restTimeMs*/,
+ connectionAcquisitionTimeMs,
+ workItems /*iterations*/,
+ SECONDS.toMillis(3) /*postTestTimeMs*/);
+
+ // hard to put exact bounds on how many thread we will use but we can put an upper bound on usage (if there was only one thread)
+ final long totalWorkTime = workItems * workTimeMs;
+ final long connectionMax = totalWorkTime / connectionAcquisitionTimeMs;
+ assertTrue(connectionMax <= counts.maxActive);
+ assertEquals(connectionMax, counts.maxTotal, 2 + 2 /*delta*/);
+ }
+
+ private Counts testPoolSize(final int minIdle, final int maxPoolSize, final int threadCount,
+ final long workTimeMs, final long restTimeMs, final long connectionAcquisitionTimeMs,
+ final int iterations, final long postTestTimeMs) throws Exception {
+
+ LOGGER.info("Starting test (minIdle={}, maxPoolSize={}, threadCount={}, workTimeMs={}, restTimeMs={}, connectionAcquisitionTimeMs={}, iterations={}, postTestTimeMs={})",
+ minIdle, maxPoolSize, threadCount, workTimeMs, restTimeMs, connectionAcquisitionTimeMs, iterations, postTestTimeMs);
+
+ final HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(minIdle);
+ config.setMaximumPoolSize(maxPoolSize);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setConnectionTimeout(2500);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ final AtomicReference<Exception> ref = new AtomicReference<>(null);
+
+ // Initialize HikariPool with no initial connections and room to grow
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+ final StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
+ // connection acquisition takes more than 0 ms in a real system
+ stubDataSource.setConnectionAcquistionTime(connectionAcquisitionTimeMs);
+
+ final ExecutorService threadPool = newFixedThreadPool(threadCount);
+ final CountDownLatch allThreadsDone = new CountDownLatch(iterations);
+ for (int i = 0; i < iterations; i++) {
+ threadPool.submit(() -> {
+ if (ref.get() == null) {
+ quietlySleep(restTimeMs);
+ try (Connection c2 = ds.getConnection()) {
+ quietlySleep(workTimeMs);
+ }
+ catch (Exception e) {
+ ref.set(e);
+ }
+ }
+ allThreadsDone.countDown();
+ });
+ }
+
+ final HikariPool pool = getPool(ds);
+
+ // collect pool usage data while work is still being done
+ final Counts underLoad = new Counts();
+ while (allThreadsDone.getCount() > 0 || pool.getTotalConnections() < minIdle) {
+ quietlySleep(50);
+ underLoad.updateMaxCounts(pool);
+ }
+
+ // wait for long enough any pending acquisitions have already been done
+ LOGGER.info("Test Over, waiting for post delay time {}ms ", postTestTimeMs);
+ quietlySleep(connectionAcquisitionTimeMs + workTimeMs + restTimeMs);
+
+ // collect pool data while there is no work to do.
+ final Counts postLoad = new Counts();
+ final long start = currentTime();
+ while (elapsedMillis(start) < postTestTimeMs) {
+ quietlySleep(50);
+ postLoad.updateMaxCounts(pool);
+ }
+
+ allThreadsDone.await();
+
+ threadPool.shutdown();
+ threadPool.awaitTermination(30, SECONDS);
+
+ if (ref.get() != null) {
+ LOGGER.error("Task failed", ref.get());
+ fail("Task failed");
+ }
+
+ LOGGER.info("Under Load... {}", underLoad);
+ LOGGER.info("Post Load.... {}", postLoad);
+
+ // verify that the no connections created after the work has stopped
+ if (postTestTimeMs > 0) {
+ if (postLoad.maxActive != 0) {
+ fail("Max Active was greater than 0 after test was done");
+ }
+
+ final int createdAfterWorkAllFinished = postLoad.maxTotal - underLoad.maxTotal;
+ assertEquals("Connections were created when there was no waiting consumers", 0, createdAfterWorkAllFinished, 1 /*delta*/);
+ }
+
+ return underLoad;
+ }
+ }
+
+ private static class Counts {
+ int maxTotal = 0;
+ int maxActive = 0;
+
+ void updateMaxCounts(final HikariPool pool) {
+ maxTotal = Math.max(pool.getTotalConnections(), maxTotal);
+ maxActive = Math.max(pool.getActiveConnections(), maxActive);
+ }
+
+ @Override
+ public String toString() {
+ return "Counts{" +
+ "maxTotal=" + maxTotal +
+ ", maxActive=" + maxActive +
+ '}';
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/ConnectionRaceConditionTest.java b/src/test/java/com/zaxxer/hikari/pool/ConnectionRaceConditionTest.java
new file mode 100755
index 0000000..31f8363
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/ConnectionRaceConditionTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jLogLevel;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.logging.log4j.Level;
+import org.junit.After;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.util.ConcurrentBag;
+
+/**
+ * @author Matthew Tambara (matthew.tambara@liferay.com)
+ */
+public class ConnectionRaceConditionTest
+{
+
+ public static final int ITERATIONS = 10_000;
+
+ @Test
+ public void testRaceCondition() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(10);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setConnectionTimeout(2500);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ setSlf4jLogLevel(ConcurrentBag.class, Level.INFO);
+
+ final AtomicReference<Exception> ref = new AtomicReference<>(null);
+
+ // Initialize HikariPool with no initial connections and room to grow
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+ ExecutorService threadPool = Executors.newFixedThreadPool(2);
+ for (int i = 0; i < ITERATIONS; i++) {
+ threadPool.submit(new Callable<Exception>() {
+ /** {@inheritDoc} */
+ @Override
+ public Exception call() throws Exception
+ {
+ if (ref.get() == null) {
+ Connection c2;
+ try {
+ c2 = ds.getConnection();
+ ds.evictConnection(c2);
+ }
+ catch (Exception e) {
+ ref.set(e);
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ threadPool.shutdown();
+ threadPool.awaitTermination(30, TimeUnit.SECONDS);
+
+ if (ref.get() != null) {
+ LoggerFactory.getLogger(ConnectionRaceConditionTest.class).error("Task failed", ref.get());
+ fail("Task failed");
+ }
+ }
+ catch (Exception e) {
+ throw e;
+ }
+ }
+
+ @After
+ public void after()
+ {
+ System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
+
+ setSlf4jLogLevel(HikariPool.class, Level.WARN);
+ setSlf4jLogLevel(ConcurrentBag.class, Level.WARN);
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/ConnectionStateTest.java b/src/test/java/com/zaxxer/hikari/pool/ConnectionStateTest.java
new file mode 100644
index 0000000..35b36fb
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/ConnectionStateTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.util.UtilityElf;
+
+public class ConnectionStateTest
+{
+ @Test
+ public void testAutoCommit() throws SQLException
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setAutoCommit(true);
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setConnectionTestQuery("VALUES 1");
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ ds.addDataSourceProperty("user", "bar");
+ ds.addDataSourceProperty("password", "secret");
+ ds.addDataSourceProperty("url", "baf");
+ ds.addDataSourceProperty("loginTimeout", "10");
+
+ try (Connection connection = ds.getConnection()) {
+ Connection unwrap = connection.unwrap(Connection.class);
+ connection.setAutoCommit(false);
+ connection.close();
+
+ assertTrue(unwrap.getAutoCommit());
+ }
+ }
+ }
+
+ @Test
+ public void testTransactionIsolation() throws SQLException
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setConnectionTestQuery("VALUES 1");
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (Connection connection = ds.getConnection()) {
+ Connection unwrap = connection.unwrap(Connection.class);
+ connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
+ connection.close();
+
+ assertEquals(Connection.TRANSACTION_READ_COMMITTED, unwrap.getTransactionIsolation());
+ }
+ }
+ }
+
+ @Test
+ public void testIsolation() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.setTransactionIsolation("TRANSACTION_REPEATABLE_READ");
+ config.validate();
+
+ int transactionIsolation = UtilityElf.getTransactionIsolation(config.getTransactionIsolation());
+ assertSame(Connection.TRANSACTION_REPEATABLE_READ, transactionIsolation);
+ }
+
+ @Test
+ public void testReadOnly() throws Exception
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setCatalog("test");
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setConnectionTestQuery("VALUES 1");
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (Connection connection = ds.getConnection()) {
+ Connection unwrap = connection.unwrap(Connection.class);
+ connection.setReadOnly(true);
+ connection.close();
+
+ assertFalse(unwrap.isReadOnly());
+ }
+ }
+ }
+
+ @Test
+ public void testCatalog() throws SQLException
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setCatalog("test");
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setConnectionTestQuery("VALUES 1");
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (Connection connection = ds.getConnection()) {
+ Connection unwrap = connection.unwrap(Connection.class);
+ connection.setCatalog("other");
+ connection.close();
+
+ assertEquals("test", unwrap.getCatalog());
+ }
+ }
+ }
+
+ @Test
+ public void testCommitTracking() throws SQLException
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setAutoCommit(false);
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setConnectionTestQuery("VALUES 1");
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (Connection connection = ds.getConnection()) {
+ Statement statement = connection.createStatement();
+ statement.execute("SELECT something");
+ assertTrue(TestElf.getConnectionCommitDirtyState(connection));
+
+ connection.commit();
+ assertFalse(TestElf.getConnectionCommitDirtyState(connection));
+
+ statement.execute("SELECT something", Statement.NO_GENERATED_KEYS);
+ assertTrue(TestElf.getConnectionCommitDirtyState(connection));
+
+ connection.rollback();
+ assertFalse(TestElf.getConnectionCommitDirtyState(connection));
+
+ ResultSet resultSet = statement.executeQuery("SELECT something");
+ assertTrue(TestElf.getConnectionCommitDirtyState(connection));
+
+ connection.rollback(null);
+ assertFalse(TestElf.getConnectionCommitDirtyState(connection));
+
+ resultSet.updateRow();
+ assertTrue(TestElf.getConnectionCommitDirtyState(connection));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/ExceptionTest.java b/src/test/java/com/zaxxer/hikari/pool/ExceptionTest.java
new file mode 100644
index 0000000..8bd64b0
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/ExceptionTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class ExceptionTest
+{
+ private HikariDataSource ds;
+
+ @Before
+ public void setup()
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(2);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ ds = new HikariDataSource(config);
+ }
+
+ @After
+ public void teardown()
+ {
+ ds.close();
+ }
+
+ @Test
+ public void testException1() throws SQLException
+ {
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+
+ PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
+ assertNotNull(statement);
+
+ ResultSet resultSet = statement.executeQuery();
+ assertNotNull(resultSet);
+
+ try {
+ statement.getMaxFieldSize();
+ fail();
+ }
+ catch (Exception e) {
+ assertSame(SQLException.class, e.getClass());
+ }
+ }
+
+ HikariPool pool = getPool(ds);
+ assertTrue("Total (3) connections not as expected", pool.getTotalConnections() >= 0);
+ assertTrue("Idle (3) connections not as expected", pool.getIdleConnections() >= 0);
+ }
+
+ @Test
+ public void testUseAfterStatementClose() throws SQLException
+ {
+ Connection connection = ds.getConnection();
+ assertNotNull(connection);
+
+ try (Statement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?")) {
+ statement.close();
+ statement.getMoreResults();
+
+ fail();
+ }
+ catch (SQLException e) {
+ assertSame("Connection is closed", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUseAfterClose() throws SQLException
+ {
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+ connection.close();
+
+ try (Statement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?")) {
+ fail();
+ }
+ catch (SQLException e) {
+ assertSame("Connection is closed", e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/HouseKeeperCleanupTest.java b/src/test/java/com/zaxxer/hikari/pool/HouseKeeperCleanupTest.java
new file mode 100644
index 0000000..1a7caa7
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/HouseKeeperCleanupTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.util.UtilityElf;
+
+/**
+ * @author Martin Stříž (striz@raynet.cz)
+ */
+public class HouseKeeperCleanupTest
+{
+
+ private ScheduledThreadPoolExecutor executor;
+
+ @Before
+ public void before() throws Exception
+ {
+ ThreadFactory threadFactory = new UtilityElf.DefaultThreadFactory("global housekeeper", true);
+
+ executor = new ScheduledThreadPoolExecutor(1, threadFactory, new ThreadPoolExecutor.DiscardPolicy());
+ executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+ executor.setRemoveOnCancelPolicy(true);
+ }
+
+ @Test
+ public void testHouseKeeperCleanupWithCustomExecutor() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(10);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setConnectionTimeout(2500);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.setScheduledExecutorService(executor);
+
+ HikariConfig config2 = newHikariConfig();
+ config.copyState(config2);
+
+ try (
+ final HikariDataSource ds1 = new HikariDataSource(config);
+ final HikariDataSource ds2 = new HikariDataSource(config2)
+ ) {
+ assertEquals("Scheduled tasks count not as expected, ", 2, executor.getQueue().size());
+ }
+
+ assertEquals("Scheduled tasks count not as expected, ", 0, executor.getQueue().size());
+ }
+
+ @After
+ public void after() throws Exception
+ {
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/IsolationTest.java b/src/test/java/com/zaxxer/hikari/pool/IsolationTest.java
new file mode 100644
index 0000000..01a80ae
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/IsolationTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariDataSource;
+
+public class IsolationTest
+{
+ @Test
+ public void testIsolation() throws SQLException
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setIsolateInternalQueries(true);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (Connection connection = ds.getConnection()) {
+ connection.close();
+
+ try (Connection connection2 = ds.getConnection()) {
+ connection2.close();
+
+ assertNotSame(connection, connection2);
+ assertSame(connection.unwrap(Connection.class), connection2.unwrap(Connection.class));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testNonIsolation() throws SQLException
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setIsolateInternalQueries(false);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (Connection connection = ds.getConnection()) {
+ connection.close();
+
+ try (Connection connection2 = ds.getConnection()) {
+ connection2.close();
+
+ assertSame(connection.unwrap(Connection.class), connection2.unwrap(Connection.class));
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/JdbcDriverTest.java b/src/test/java/com/zaxxer/hikari/pool/JdbcDriverTest.java
new file mode 100644
index 0000000..0d9cc2f
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/JdbcDriverTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.util.DriverDataSource;
+
+public class JdbcDriverTest
+{
+ private HikariDataSource ds;
+
+ @After
+ public void teardown()
+ {
+ if (ds != null) {
+ ds.close();
+ }
+ }
+
+ @Test
+ public void driverTest1() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDriverClassName("com.zaxxer.hikari.mocks.StubDriver");
+ config.setJdbcUrl("jdbc:stub");
+ config.addDataSourceProperty("user", "bart");
+ config.addDataSourceProperty("password", "simpson");
+
+ ds = new HikariDataSource(config);
+
+ assertTrue(ds.isWrapperFor(DriverDataSource.class));
+
+ DriverDataSource unwrap = ds.unwrap(DriverDataSource.class);
+ assertNotNull(unwrap);
+
+ try (Connection connection = ds.getConnection()) {
+ // test that getConnection() succeeds
+ }
+ }
+
+ @Test
+ public void driverTest2() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDriverClassName("com.zaxxer.hikari.mocks.StubDriver");
+ config.setJdbcUrl("jdbc:invalid");
+
+ try {
+ ds = new HikariDataSource(config);
+ }
+ catch (RuntimeException e) {
+ assertTrue(e.getMessage().contains("claims to not accept"));
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/MiscTest.java b/src/test/java/com/zaxxer/hikari/pool/MiscTest.java
new file mode 100644
index 0000000..f9698ef
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/MiscTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.setConfigUnitTest;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jLogLevel;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jTargetStream;
+import static com.zaxxer.hikari.util.UtilityElf.createInstance;
+import static com.zaxxer.hikari.util.UtilityElf.getTransactionIsolation;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.Level;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+/**
+ * @author Brett Wooldridge
+ */
+public class MiscTest
+{
+ @Test
+ public void testLogWriter() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(4);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ setConfigUnitTest(true);
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ PrintWriter writer = new PrintWriter(System.out);
+ ds.setLogWriter(writer);
+ assertSame(writer, ds.getLogWriter());
+ assertEquals("testLogWriter", config.getPoolName());
+ }
+ finally
+ {
+ setConfigUnitTest(false);
+ }
+ }
+
+ @Test
+ public void testInvalidIsolation()
+ {
+ try {
+ getTransactionIsolation("INVALID");
+ fail();
+ }
+ catch (Exception e) {
+ assertTrue(e instanceof IllegalArgumentException);
+ }
+ }
+
+ @Test
+ public void testCreateInstance()
+ {
+ try {
+ createInstance("invalid", null);
+ fail();
+ }
+ catch (RuntimeException e) {
+ assertTrue(e.getCause() instanceof ClassNotFoundException);
+ }
+ }
+
+ @Test
+ public void testLeakDetection() throws Exception
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (PrintStream ps = new PrintStream(baos, true)) {
+ setSlf4jTargetStream(Class.forName("com.zaxxer.hikari.pool.ProxyLeakTask"), ps);
+ setConfigUnitTest(true);
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(4);
+ config.setThreadFactory(Executors.defaultThreadFactory());
+ config.setMetricRegistry(null);
+ config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(1));
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
+ getPool(ds).logPoolState();
+
+ try (Connection connection = ds.getConnection()) {
+ quietlySleep(SECONDS.toMillis(4));
+ connection.close();
+ quietlySleep(SECONDS.toMillis(1));
+ ps.close();
+ String s = new String(baos.toByteArray());
+ assertNotNull("Exception string was null", s);
+ assertTrue("Expected exception to contain 'Connection leak detection' but contains *" + s + "*", s.contains("Connection leak detection"));
+ }
+ }
+ finally
+ {
+ setConfigUnitTest(false);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/PostgresTest.java b/src/test/java/com/zaxxer/hikari/pool/PostgresTest.java
new file mode 100644
index 0000000..693e7b3
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/PostgresTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.util.ClockSource.currentTime;
+import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.util.UtilityElf;
+
+/**
+ * This test is meant to be run manually and interactively and was
+ * build for issue #159.
+ *
+ * @author Brett Wooldridge
+ */
+public class PostgresTest
+{
+ //@Test
+ public void testCase1() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(3);
+ config.setMaximumPoolSize(10);
+ config.setConnectionTimeout(3000);
+ config.setIdleTimeout(TimeUnit.SECONDS.toMillis(10));
+ config.setValidationTimeout(TimeUnit.SECONDS.toMillis(2));
+
+ config.setJdbcUrl("jdbc:pgsql://localhost:5432/test");
+ config.setUsername("brettw");
+
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+ final long start = currentTime();
+ do {
+ Thread t = new Thread() {
+ public void run() {
+ try (Connection connection = ds.getConnection()) {
+ System.err.println("Obtained connection " + connection);
+ quietlySleep(TimeUnit.SECONDS.toMillis((long)(10 + (Math.random() * 20))));
+ }
+ catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ t.setDaemon(true);
+ t.start();
+
+ quietlySleep(TimeUnit.SECONDS.toMillis((long)((Math.random() * 20))));
+ } while (elapsedMillis(start) < MINUTES.toMillis(15));
+ }
+ }
+
+ //@Test
+ public void testCase2() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(3);
+ config.setMaximumPoolSize(10);
+ config.setConnectionTimeout(1000);
+ config.setIdleTimeout(TimeUnit.SECONDS.toMillis(60));
+
+ config.setJdbcUrl("jdbc:pgsql://localhost:5432/test");
+ config.setUsername("brettw");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+
+ try (Connection conn = ds.getConnection()) {
+ System.err.println("\nGot a connection, and released it. Now, enable the firewall.");
+ }
+
+ getPool(ds).logPoolState();
+ quietlySleep(5000L);
+
+ System.err.println("\nNow attempting another getConnection(), expecting a timeout...");
+
+ long start = currentTime();
+ try (Connection conn = ds.getConnection()) {
+ System.err.println("\nOpps, got a connection. Did you enable the firewall? " + conn);
+ fail("Opps, got a connection. Did you enable the firewall?");
+ }
+ catch (SQLException e)
+ {
+ assertTrue("Timeout less than expected " + elapsedMillis(start) + "ms", elapsedMillis(start) > 5000);
+ }
+
+ System.err.println("\nOk, so far so good. Now, disable the firewall again. Attempting connection in 5 seconds...");
+ quietlySleep(5000L);
+ getPool(ds).logPoolState();
+
+ try (Connection conn = ds.getConnection()) {
+ System.err.println("\nGot a connection, and released it.");
+ }
+ }
+
+ System.err.println("\nPassed.");
+ }
+
+ //@Test
+ public void testCase3() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(3);
+ config.setMaximumPoolSize(10);
+ config.setConnectionTimeout(1000);
+ config.setIdleTimeout(TimeUnit.SECONDS.toMillis(60));
+
+ config.setJdbcUrl("jdbc:pgsql://localhost:5432/test");
+ config.setUsername("brettw");
+
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+ for (int i = 0; i < 10; i++) {
+ new Thread() {
+ public void run() {
+ try (Connection conn = ds.getConnection()) {
+ System.err.println("ERROR: should not have acquired connection.");
+ }
+ catch (SQLException e) {
+ // expected
+ }
+ }
+ }.start();
+ }
+
+ quietlySleep(5000L);
+
+ System.err.println("Now, bring the DB online. Checking pool in 15 seconds.");
+ quietlySleep(15000L);
+
+ getPool(ds).logPoolState();
+ }
+ }
+
+ // @Test
+ public void testCase4() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(15);
+ config.setConnectionTimeout(10000);
+ config.setIdleTimeout(TimeUnit.MINUTES.toMillis(1));
+ config.setMaxLifetime(TimeUnit.MINUTES.toMillis(2));
+ config.setRegisterMbeans(true);
+
+ config.setJdbcUrl("jdbc:postgresql://localhost:5432/netld");
+ config.setUsername("brettw");
+
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+
+ countdown(20);
+ List<Thread> threads = new ArrayList<>();
+ for (int i = 0; i < 20; i++) {
+ threads.add(new Thread() {
+ public void run() {
+ UtilityElf.quietlySleep((long)(Math.random() * 2500L));
+ final long start = currentTime();
+ do {
+ try (Connection conn = ds.getConnection(); Statement stmt = conn.createStatement()) {
+ try (ResultSet rs = stmt.executeQuery("SELECT * FROM device WHERE device_id=0 ORDER BY device_id LIMIT 1 OFFSET 0")) {
+ rs.next();
+ }
+ UtilityElf.quietlySleep(100L); //Math.max(50L, (long)(Math.random() * 250L)));
+ }
+ catch (SQLException e) {
+ e.printStackTrace();
+ // throw new RuntimeException(e);
+ }
+
+ // UtilityElf.quietlySleep(10L); //Math.max(50L, (long)(Math.random() * 250L)));
+ } while (elapsedMillis(start) < TimeUnit.MINUTES.toMillis(5));
+ }
+ });
+ }
+
+// threads.forEach(t -> t.start());
+// threads.forEach(t -> { try { t.join(); } catch (InterruptedException e) {} });
+ }
+ }
+
+ @Before
+ public void before()
+ {
+ System.err.println("\n");
+ }
+
+ private void countdown(int seconds)
+ {
+ do {
+ System.out.printf("Starting in %d seconds...\n", seconds);
+ if (seconds > 10) {
+ UtilityElf.quietlySleep(TimeUnit.SECONDS.toMillis(10));
+ seconds -= 10;
+ }
+ else {
+ UtilityElf.quietlySleep(TimeUnit.SECONDS.toMillis(1));
+ seconds -= 1;
+ }
+ } while (seconds > 0);
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/RampUpDown.java b/src/test/java/com/zaxxer/hikari/pool/RampUpDown.java
new file mode 100755
index 0000000..e09894d
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/RampUpDown.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static org.junit.Assert.assertSame;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class RampUpDown
+{
+ @Test
+ public void rampUpDownTest() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(60);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "250");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+
+ ds.setIdleTimeout(1000);
+ HikariPool pool = getPool(ds);
+
+ // wait two housekeeping periods so we don't fail if this part of test runs too quickly
+ quietlySleep(500);
+
+ Assert.assertSame("Total connections not as expected", 5, pool.getTotalConnections());
+
+ Connection[] connections = new Connection[ds.getMaximumPoolSize()];
+ for (int i = 0; i < connections.length; i++)
+ {
+ connections[i] = ds.getConnection();
+ }
+
+ assertSame("Total connections not as expected", 60, pool.getTotalConnections());
+
+ for (Connection connection : connections)
+ {
+ connection.close();
+ }
+
+ quietlySleep(500);
+
+ assertSame("Total connections not as expected", 5, pool.getTotalConnections());
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/ShutdownTest.java b/src/test/java/com/zaxxer/hikari/pool/ShutdownTest.java
new file mode 100644
index 0000000..aa12a43
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/ShutdownTest.java
@@ -0,0 +1,356 @@
+/*
+ * 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.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jLogLevel;
+import static com.zaxxer.hikari.util.ClockSource.currentTime;
+import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.apache.logging.log4j.Level;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubConnection;
+import com.zaxxer.hikari.util.UtilityElf;
+
+/**
+ * @author Brett Wooldridge
+ */
+public class ShutdownTest
+{
+ @Before
+ public void beforeTest()
+ {
+ setSlf4jLogLevel(PoolBase.class, Level.DEBUG);
+ setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
+ StubConnection.count.set(0);
+ }
+
+ @After
+ public void afterTest()
+ {
+ setSlf4jLogLevel(PoolBase.class, Level.WARN);
+ setSlf4jLogLevel(HikariPool.class, Level.WARN);
+ StubConnection.slowCreate = false;
+ }
+
+ @Test
+ public void testShutdown1() throws SQLException
+ {
+ Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
+
+ StubConnection.slowCreate = true;
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(10);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ HikariPool pool = getPool(ds);
+
+ Thread[] threads = new Thread[10];
+ for (int i = 0; i < 10; i++) {
+ threads[i] = new Thread() {
+ @Override
+ public void run()
+ {
+ try {
+ if (ds.getConnection() != null) {
+ quietlySleep(SECONDS.toMillis(1));
+ }
+ }
+ catch (SQLException e) {
+ }
+ }
+ };
+ threads[i].setDaemon(true);
+ }
+ for (int i = 0; i < 10; i++) {
+ threads[i].start();
+ }
+
+ quietlySleep(1800L);
+
+ assertTrue("Total connection count not as expected, ", pool.getTotalConnections() > 0);
+
+ ds.close();
+
+ assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
+ assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
+ assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
+ assertTrue(ds.isClosed());
+ }
+ }
+
+ @Test
+ public void testShutdown2() throws SQLException
+ {
+ assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
+
+ StubConnection.slowCreate = true;
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(10);
+ config.setMaximumPoolSize(10);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ HikariPool pool = getPool(ds);
+
+ quietlySleep(1200L);
+
+ assertTrue("Total connection count not as expected, ", pool.getTotalConnections() > 0);
+
+ ds.close();
+
+ assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
+ assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
+ assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
+ assertTrue(ds.toString().startsWith("HikariDataSource (") && ds.toString().endsWith(")"));
+ }
+ }
+
+ @Test
+ public void testShutdown3() throws SQLException
+ {
+ assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
+
+ StubConnection.slowCreate = false;
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(5);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ HikariPool pool = getPool(ds);
+
+ quietlySleep(1200L);
+
+ assertTrue("Total connection count not as expected, ", pool.getTotalConnections() == 5);
+
+ ds.close();
+
+ assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
+ assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
+ assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
+ }
+ }
+
+ @Test
+ public void testShutdown4() throws SQLException
+ {
+ StubConnection.slowCreate = true;
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(10);
+ config.setMaximumPoolSize(10);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ quietlySleep(500L);
+
+ ds.close();
+
+ long startTime = currentTime();
+ while (elapsedMillis(startTime) < SECONDS.toMillis(5) && threadCount() > 0) {
+ quietlySleep(250);
+ }
+
+ assertSame("Unreleased connections after shutdown", 0, getPool(ds).getTotalConnections());
+ }
+ }
+
+ @Test
+ public void testShutdown5() throws SQLException
+ {
+ Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(5);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ HikariPool pool = getPool(ds);
+
+ Connection[] connections = new Connection[5];
+ for (int i = 0; i < 5; i++) {
+ connections[i] = ds.getConnection();
+ }
+
+ Assert.assertTrue("Total connection count not as expected, ", pool.getTotalConnections() == 5);
+
+ ds.close();
+
+ Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
+ Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
+ Assert.assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
+ }
+ }
+
+ @Test
+ public void testAfterShutdown() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(5);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ ds.close();
+ try {
+ ds.getConnection();
+ }
+ catch (SQLException e) {
+ Assert.assertTrue(e.getMessage().contains("has been closed."));
+ }
+ }
+ }
+
+ @Test
+ public void testShutdownDuringInit() throws Exception
+ {
+ final HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(5);
+ config.setConnectionTimeout(1000);
+ config.setValidationTimeout(1000);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ StubConnection.slowCreate = true;
+ UtilityElf.quietlySleep(3000L);
+ }
+ }
+
+ @Test
+ public void testThreadedShutdown() throws Exception
+ {
+ final HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(5);
+ config.setConnectionTimeout(1000);
+ config.setValidationTimeout(1000);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ for (int i = 0; i < 4; i++) {
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+ Thread t = new Thread() {
+ @Override
+ public void run()
+ {
+ try (Connection connection = ds.getConnection()) {
+ for (int i = 0; i < 10; i++) {
+ Connection connection2 = null;
+ try {
+ connection2 = ds.getConnection();
+ PreparedStatement stmt = connection2.prepareStatement("SOMETHING");
+ UtilityElf.quietlySleep(20);
+ stmt.getMaxFieldSize();
+ }
+ catch (SQLException e) {
+ try {
+ if (connection2 != null) {
+ connection2.close();
+ }
+ }
+ catch (SQLException e2) {
+ if (e2.getMessage().contains("shutdown") || e2.getMessage().contains("evicted")) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ Assert.fail(e.getMessage());
+ }
+ finally {
+ ds.close();
+ }
+ }
+ };
+ t.start();
+
+ Thread t2 = new Thread() {
+ @Override
+ public void run()
+ {
+ UtilityElf.quietlySleep(100);
+ try {
+ ds.close();
+ }
+ catch (IllegalStateException e) {
+ Assert.fail(e.getMessage());
+ }
+ }
+ };
+ t2.start();
+
+ t.join();
+ t2.join();
+
+ ds.close();
+ }
+ }
+ }
+
+ private int threadCount()
+ {
+ Thread[] threads = new Thread[Thread.activeCount() * 2];
+ Thread.enumerate(threads);
+
+ int count = 0;
+ for (Thread thread : threads) {
+ count += (thread != null && thread.getName().startsWith("Hikari")) ? 1 : 0;
+ }
+
+ return count;
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/StatementTest.java b/src/test/java/com/zaxxer/hikari/pool/StatementTest.java
new file mode 100644
index 0000000..ebe9c90
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/StatementTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class StatementTest
+{
+ private HikariDataSource ds;
+
+ @Before
+ public void setup()
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(2);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ ds = new HikariDataSource(config);
+ }
+
+ @After
+ public void teardown()
+ {
+ ds.close();
+ }
+
+ @Test
+ public void testStatementClose() throws SQLException
+ {
+ ds.getConnection().close();
+
+ HikariPool pool = getPool(ds);
+ assertTrue("Total connections not as expected", pool.getTotalConnections() >= 1);
+ assertTrue("Idle connections not as expected", pool.getIdleConnections() >= 1);
+
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+
+ assertTrue("Total connections not as expected", pool.getTotalConnections() >= 1);
+ assertTrue("Idle connections not as expected", pool.getIdleConnections() >= 0);
+
+ Statement statement = connection.createStatement();
+ assertNotNull(statement);
+
+ connection.close();
+
+ assertTrue(statement.isClosed());
+ }
+ }
+
+ @Test
+ public void testAutoStatementClose() throws SQLException
+ {
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+
+ Statement statement1 = connection.createStatement();
+ assertNotNull(statement1);
+ Statement statement2 = connection.createStatement();
+ assertNotNull(statement2);
+
+ connection.close();
+
+ assertTrue(statement1.isClosed());
+ assertTrue(statement2.isClosed());
+ }
+ }
+
+ @Test
+ public void testDoubleStatementClose() throws SQLException
+ {
+ try (Connection connection = ds.getConnection();
+ Statement statement1 = connection.createStatement()) {
+ statement1.close();
+ statement1.close();
+ }
+ }
+
+ @Test
+ public void testOutOfOrderStatementClose() throws SQLException
+ {
+ try (Connection connection = ds.getConnection();
+ Statement statement1 = connection.createStatement();
+ Statement statement2 = connection.createStatement()) {
+ statement1.close();
+ statement2.close();
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestConcurrentBag.java b/src/test/java/com/zaxxer/hikari/pool/TestConcurrentBag.java
new file mode 100644
index 0000000..b8d9d22
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestConcurrentBag.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jTargetStream;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.concurrent.CompletableFuture;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.util.ConcurrentBag;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class TestConcurrentBag
+{
+ private static HikariDataSource ds;
+ private static HikariPool pool;
+
+ @BeforeClass
+ public static void setup()
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(2);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ ds = new HikariDataSource(config);
+ pool = getPool(ds);
+ }
+
+ @AfterClass
+ public static void teardown()
+ {
+ ds.close();
+ }
+
+ @Test
+ public void testConcurrentBag() throws Exception
+ {
+ try (ConcurrentBag<PoolEntry> bag = new ConcurrentBag<>((x) -> CompletableFuture.completedFuture(Boolean.TRUE))) {
+ assertEquals(0, bag.values(8).size());
+
+ PoolEntry reserved = pool.newPoolEntry();
+ bag.add(reserved);
+ bag.reserve(reserved); // reserved
+
+ PoolEntry inuse = pool.newPoolEntry();
+ bag.add(inuse);
+ bag.borrow(2, MILLISECONDS); // in use
+
+ PoolEntry notinuse = pool.newPoolEntry();
+ bag.add(notinuse); // not in use
+
+ bag.dumpState();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos, true);
+ setSlf4jTargetStream(ConcurrentBag.class, ps);
+
+ bag.requite(reserved);
+
+ bag.remove(notinuse);
+ assertTrue(new String(baos.toByteArray()).contains("not borrowed or reserved"));
+
+ bag.unreserve(notinuse);
+ assertTrue(new String(baos.toByteArray()).contains("was not reserved"));
+
+ bag.remove(inuse);
+ bag.remove(inuse);
+ assertTrue(new String(baos.toByteArray()).contains("not borrowed or reserved"));
+
+ bag.close();
+ try {
+ PoolEntry bagEntry = pool.newPoolEntry();
+ bag.add(bagEntry);
+ assertNotEquals(bagEntry, bag.borrow(100, MILLISECONDS));
+ }
+ catch (IllegalStateException e) {
+ assertTrue(new String(baos.toByteArray()).contains("ignoring add()"));
+ }
+
+ assertNotNull(notinuse.toString());
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestConnectionCloseBlocking.java b/src/test/java/com/zaxxer/hikari/pool/TestConnectionCloseBlocking.java
new file mode 100644
index 0000000..8d98f83
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestConnectionCloseBlocking.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+/**
+ *
+ */
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.util.ClockSource.currentTime;
+import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.MockDataSource;
+
+/**
+ * Test for cases when db network connectivity goes down and close is called on existing connections. By default Hikari
+ * blocks longer than getMaximumTimeout (it can hang for a lot of time depending on driver timeout settings). Closing
+ * async the connections fixes this issue.
+ *
+ */
+public class TestConnectionCloseBlocking {
+ private static volatile boolean shouldFail = false;
+
+ // @Test
+ public void testConnectionCloseBlocking() throws SQLException {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(1500);
+ config.setDataSource(new CustomMockDataSource());
+
+ long start = currentTime();
+ try (HikariDataSource ds = new HikariDataSource(config);
+ Connection connection = ds.getConnection()) {
+
+ connection.close();
+
+ // Hikari only checks for validity for connections with lastAccess > 1000 ms so we sleep for 1001 ms to force
+ // Hikari to do a connection validation which will fail and will trigger the connection to be closed
+ quietlySleep(1100L);
+
+ shouldFail = true;
+
+ // on physical connection close we sleep 2 seconds
+ try (Connection connection2 = ds.getConnection()) {
+ assertTrue("Waited longer than timeout", (elapsedMillis(start) < config.getConnectionTimeout()));
+ }
+ } catch (SQLException e) {
+ assertTrue("getConnection failed because close connection took longer than timeout", (elapsedMillis(start) < config.getConnectionTimeout()));
+ }
+ }
+
+ private static class CustomMockDataSource extends MockDataSource {
+ @Override
+ public Connection getConnection() throws SQLException {
+ Connection mockConnection = super.getConnection();
+ when(mockConnection.isValid(anyInt())).thenReturn(!shouldFail);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ if (shouldFail) {
+ SECONDS.sleep(2);
+ }
+ return null;
+ }
+ }).when(mockConnection).close();
+ return mockConnection;
+ }
+ }
+
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestConnectionTimeoutRetry.java b/src/test/java/com/zaxxer/hikari/pool/TestConnectionTimeoutRetry.java
new file mode 100644
index 0000000..15f8974
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestConnectionTimeoutRetry.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jLogLevel;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jTargetStream;
+import static com.zaxxer.hikari.util.ClockSource.currentTime;
+import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
+import static java.lang.Thread.sleep;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.Level;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubConnection;
+import com.zaxxer.hikari.mocks.StubDataSource;
+
+public class TestConnectionTimeoutRetry
+{
+ @Test
+ public void testConnectionRetries() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(2800);
+ config.setValidationTimeout(2800);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
+ stubDataSource.setThrowException(new SQLException("Connection refused"));
+
+ long start = currentTime();
+ try (Connection connection = ds.getConnection()) {
+ connection.close();
+ fail("Should not have been able to get a connection.");
+ }
+ catch (SQLException e) {
+ long elapsed = elapsedMillis(start);
+ long timeout = config.getConnectionTimeout();
+ assertTrue("Didn't wait long enough for timeout", (elapsed >= timeout));
+ }
+ }
+ }
+
+ @Test
+ public void testConnectionRetries2() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(2800);
+ config.setValidationTimeout(2800);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ final StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
+ stubDataSource.setThrowException(new SQLException("Connection refused"));
+
+ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ scheduler.schedule(new Runnable() {
+ @Override
+ public void run()
+ {
+ stubDataSource.setThrowException(null);
+ }
+ }, 300, TimeUnit.MILLISECONDS);
+
+ long start = currentTime();
+ try {
+ try (Connection connection = ds.getConnection()) {
+ // close immediately
+ }
+
+ long elapsed = elapsedMillis(start);
+ assertTrue("Connection returned too quickly, something is wrong.", elapsed > 250);
+ assertTrue("Waited too long to get a connection.", elapsed < config.getConnectionTimeout());
+ }
+ catch (SQLException e) {
+ fail("Should not have timed out: " + e.getMessage());
+ }
+ finally {
+ scheduler.shutdownNow();
+ }
+ }
+ }
+
+ @Test
+ public void testConnectionRetries3() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(2);
+ config.setConnectionTimeout(2800);
+ config.setValidationTimeout(2800);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ final Connection connection1 = ds.getConnection();
+ final Connection connection2 = ds.getConnection();
+ assertNotNull(connection1);
+ assertNotNull(connection2);
+
+ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
+ scheduler.schedule(new Runnable() {
+ @Override
+ public void run()
+ {
+ try {
+ connection1.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.err);
+ }
+ }
+ }, 800, MILLISECONDS);
+
+ long start = currentTime();
+ try {
+ try (Connection connection3 = ds.getConnection()) {
+ // close immediately
+ }
+
+ long elapsed = elapsedMillis(start);
+ assertTrue("Waited too long to get a connection.", (elapsed >= 700) && (elapsed < 950));
+ }
+ catch (SQLException e) {
+ fail("Should not have timed out.");
+ }
+ finally {
+ scheduler.shutdownNow();
+ }
+ }
+ }
+
+ @Test
+ public void testConnectionRetries5() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(2);
+ config.setConnectionTimeout(1000);
+ config.setValidationTimeout(1000);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ final Connection connection1 = ds.getConnection();
+
+ long start = currentTime();
+
+ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
+ scheduler.schedule(new Runnable() {
+ @Override
+ public void run()
+ {
+ try {
+ connection1.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.err);
+ }
+ }
+ }, 250, MILLISECONDS);
+
+ StubDataSource stubDataSource = ds.unwrap(StubDataSource.class);
+ stubDataSource.setThrowException(new SQLException("Connection refused"));
+
+ try {
+ try (Connection connection2 = ds.getConnection()) {
+ // close immediately
+ }
+
+ long elapsed = elapsedMillis(start);
+ assertTrue("Waited too long to get a connection.", (elapsed >= 250) && (elapsed < config.getConnectionTimeout()));
+ }
+ catch (SQLException e) {
+ fail("Should not have timed out.");
+ }
+ finally {
+ scheduler.shutdownNow();
+ }
+ }
+ }
+
+ @Test
+ public void testConnectionIdleFill() throws Exception
+ {
+ StubConnection.slowCreate = false;
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(5);
+ config.setMaximumPoolSize(10);
+ config.setConnectionTimeout(2000);
+ config.setValidationTimeout(2000);
+ config.setConnectionTestQuery("VALUES 2");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "400");
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos, true);
+ setSlf4jTargetStream(HikariPool.class, ps);
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
+
+ HikariPool pool = getPool(ds);
+ try (
+ Connection connection1 = ds.getConnection();
+ Connection connection2 = ds.getConnection();
+ Connection connection3 = ds.getConnection();
+ Connection connection4 = ds.getConnection();
+ Connection connection5 = ds.getConnection();
+ Connection connection6 = ds.getConnection();
+ Connection connection7 = ds.getConnection()) {
+
+ sleep(1300);
+
+ assertSame("Total connections not as expected", 10, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 3, pool.getIdleConnections());
+ }
+
+ assertSame("Total connections not as expected", 10, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 10, pool.getIdleConnections());
+ }
+ }
+
+ @Before
+ public void before()
+ {
+ setSlf4jLogLevel(HikariPool.class, Level.INFO);
+ }
+
+ @After
+ public void after()
+ {
+ System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestConnections.java b/src/test/java/com/zaxxer/hikari/pool/TestConnections.java
new file mode 100644
index 0000000..538d9ac
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestConnections.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static com.zaxxer.hikari.pool.TestElf.setConfigUnitTest;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jLogLevel;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jTargetStream;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLTransientConnectionException;
+import java.sql.Statement;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.logging.log4j.Level;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubConnection;
+import com.zaxxer.hikari.mocks.StubDataSource;
+import com.zaxxer.hikari.mocks.StubStatement;
+import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
+
+/**
+ * @author Brett Wooldridge
+ */
+public class TestConnections
+{
+ @Before
+ public void before()
+ {
+ setSlf4jTargetStream(HikariPool.class, System.err);
+ setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
+ setSlf4jLogLevel(PoolBase.class, Level.DEBUG);
+ }
+
+ @After
+ public void after()
+ {
+ System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
+ setSlf4jLogLevel(HikariPool.class, Level.WARN);
+ setSlf4jLogLevel(PoolBase.class, Level.WARN);
+ }
+
+ @Test
+ public void testCreate() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setConnectionInitSql("SELECT 1");
+ config.setReadOnly(true);
+ config.setConnectionTimeout(2500);
+ config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(30));
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ ds.setLoginTimeout(10);
+ assertSame(10, ds.getLoginTimeout());
+
+ HikariPool pool = getPool(ds);
+ ds.getConnection().close();
+ assertSame("Total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
+
+ try (Connection connection = ds.getConnection();
+ PreparedStatement statement = connection.prepareStatement("SELECT * FROM device WHERE device_id=?")) {
+
+ assertNotNull(connection);
+ assertNotNull(statement);
+
+ assertSame("Total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
+
+ statement.setInt(1, 0);
+
+ try (ResultSet resultSet = statement.executeQuery()) {
+ assertNotNull(resultSet);
+
+ assertFalse(resultSet.next());
+ }
+ }
+
+ assertSame("Total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
+ }
+ }
+
+ @Test
+ public void testMaxLifetime() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(2500);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100");
+
+ setConfigUnitTest(true);
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ System.clearProperty("com.zaxxer.hikari.housekeeping.periodMs");
+
+ ds.setMaxLifetime(700);
+
+ HikariPool pool = getPool(ds);
+
+ assertSame("Total connections not as expected", 0, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
+
+ Connection unwrap;
+ Connection unwrap2;
+ try (Connection connection = ds.getConnection()) {
+ unwrap = connection.unwrap(Connection.class);
+ assertNotNull(connection);
+
+ assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
+ }
+
+ assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
+
+ try (Connection connection = ds.getConnection()) {
+ unwrap2 = connection.unwrap(Connection.class);
+ assertSame(unwrap, unwrap2);
+ assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
+ }
+
+ quietlySleep(TimeUnit.SECONDS.toMillis(2));
+
+ try (Connection connection = ds.getConnection()) {
+ unwrap2 = connection.unwrap(Connection.class);
+ assertNotSame("Expected a different connection", unwrap, unwrap2);
+ }
+
+ assertSame("Post total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Post idle connections not as expected", 1, pool.getIdleConnections());
+ }
+ finally {
+ setConfigUnitTest(false);
+ }
+ }
+
+ @Test
+ public void testMaxLifetime2() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(2500);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100");
+
+ setConfigUnitTest(true);
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ ds.setMaxLifetime(700);
+
+ HikariPool pool = getPool(ds);
+ assertSame("Total connections not as expected", 0, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
+
+ Connection unwrap;
+ Connection unwrap2;
+ try (Connection connection = ds.getConnection()) {
+ unwrap = connection.unwrap(Connection.class);
+ assertNotNull(connection);
+
+ assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
+ }
+
+ assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
+
+ try (Connection connection = ds.getConnection()) {
+ unwrap2 = connection.unwrap(Connection.class);
+ assertSame(unwrap, unwrap2);
+ assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
+ }
+
+ quietlySleep(800);
+
+ try (Connection connection = ds.getConnection()) {
+ unwrap2 = connection.unwrap(Connection.class);
+ assertNotSame("Expected a different connection", unwrap, unwrap2);
+ }
+
+ assertSame("Post total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Post idle connections not as expected", 1, pool.getIdleConnections());
+ }
+ finally {
+ setConfigUnitTest(false);
+ }
+ }
+
+ @Test
+ public void testDoubleClose() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(2500);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config);
+ Connection connection = ds.getConnection()) {
+ connection.close();
+
+ // should no-op
+ connection.abort(null);
+
+ assertTrue("Connection should have closed", connection.isClosed());
+ assertFalse("Connection should have closed", connection.isValid(5));
+ assertTrue("Expected to contain ClosedConnection, but was " + connection, connection.toString().contains("ClosedConnection"));
+ }
+ }
+
+ @Test
+ public void testEviction() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(5);
+ config.setConnectionTimeout(2500);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ Connection connection = ds.getConnection();
+
+ HikariPool pool = getPool(ds);
+ assertEquals(1, pool.getTotalConnections());
+ ds.evictConnection(connection);
+ assertEquals(0, pool.getTotalConnections());
+ }
+ }
+
+ @Test
+ public void testBackfill() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(4);
+ config.setConnectionTimeout(1000);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ StubConnection.slowCreate = true;
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+
+ HikariPool pool = getPool(ds);
+ quietlySleep(1250);
+
+ assertSame("Total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
+
+ // This will take the pool down to zero
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+
+ assertSame("Total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
+
+ PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
+ assertNotNull(statement);
+
+ ResultSet resultSet = statement.executeQuery();
+ assertNotNull(resultSet);
+
+ try {
+ statement.getMaxFieldSize();
+ fail();
+ }
+ catch (Exception e) {
+ assertSame(SQLException.class, e.getClass());
+ }
+
+ pool.logPoolState("testBackfill() before close...");
+
+ // The connection will be ejected from the pool here
+ }
+
+ assertSame("Total connections not as expected", 0, pool.getTotalConnections());
+
+ pool.logPoolState("testBackfill() after close...");
+
+ quietlySleep(1250);
+
+ assertSame("Total connections not as expected", 1, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
+ }
+ finally {
+ StubConnection.slowCreate = false;
+ }
+ }
+
+ @Test
+ public void testMaximumPoolLimit() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(4);
+ config.setConnectionTimeout(20000);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ final AtomicReference<Exception> ref = new AtomicReference<>();
+
+ StubConnection.count.set(0); // reset counter
+
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+
+ final HikariPool pool = getPool(ds);
+
+ Thread[] threads = new Thread[20];
+ for (int i = 0; i < threads.length; i++) {
+ threads[i] = new Thread(new Runnable() {
+ @Override
+ public void run()
+ {
+ try {
+ pool.logPoolState("Before acquire ");
+ try (Connection connection = ds.getConnection()) {
+ pool.logPoolState("After acquire ");
+ quietlySleep(500);
+ }
+ }
+ catch (Exception e) {
+ ref.set(e);
+ }
+ }
+ });
+ }
+
+ for (int i = 0; i < threads.length; i++) {
+ threads[i].start();
+ }
+
+ for (int i = 0; i < threads.length; i++) {
+ threads[i].join();
+ }
+
+ pool.logPoolState("before check ");
+ assertNull((ref.get() != null ? ref.get().toString() : ""), ref.get());
+ assertSame("StubConnection count not as expected", 4, StubConnection.count.get());
+ }
+ }
+
+ @Test
+ public void testOldDriver() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(2500);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ StubConnection.oldDriver = true;
+ StubStatement.oldDriver = true;
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ quietlySleep(500);
+
+ try (Connection connection = ds.getConnection()) {
+ // close
+ }
+
+ quietlySleep(500);
+ try (Connection connection = ds.getConnection()) {
+ // close
+ }
+ }
+ finally {
+ StubConnection.oldDriver = false;
+ StubStatement.oldDriver = false;
+ }
+ }
+
+ @Test
+ public void testSuspendResume() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(3);
+ config.setMaximumPoolSize(3);
+ config.setConnectionTimeout(2500);
+ config.setAllowPoolSuspension(true);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (final HikariDataSource ds = new HikariDataSource(config)) {
+ HikariPool pool = getPool(ds);
+ while (pool.getTotalConnections() < 3) {
+ quietlySleep(50);
+ }
+
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run()
+ {
+ try {
+ ds.getConnection();
+ ds.getConnection();
+ }
+ catch (Exception e) {
+ fail();
+ }
+ }
+ });
+
+ try (Connection c3 = ds.getConnection()) {
+ assertEquals(2, pool.getIdleConnections());
+
+ pool.suspendPool();
+ t.start();
+
+ quietlySleep(500);
+ assertEquals(2, pool.getIdleConnections());
+ }
+ assertEquals(3, pool.getIdleConnections());
+ pool.resumePool();
+ quietlySleep(500);
+ assertEquals(1, pool.getIdleConnections());
+ }
+ }
+
+ @Test
+ public void testInitializationFailure1() throws SQLException
+ {
+ StubDataSource stubDataSource = new StubDataSource();
+ stubDataSource.setThrowException(new SQLException("Connection refused"));
+
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMinimumIdle(1);
+ ds.setMaximumPoolSize(1);
+ ds.setConnectionTimeout(2500);
+ ds.setConnectionTestQuery("VALUES 1");
+ ds.setDataSource(stubDataSource);
+
+ try (Connection c = ds.getConnection()) {
+ fail("Initialization should have failed");
+ }
+ catch (SQLException e) {
+ // passed
+ }
+ }
+ }
+
+ @Test
+ public void testInitializationFailure2() throws SQLException
+ {
+ StubDataSource stubDataSource = new StubDataSource();
+ stubDataSource.setThrowException(new SQLException("Connection refused"));
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSource(stubDataSource);
+
+ try (HikariDataSource ds = new HikariDataSource(config);
+ Connection c = ds.getConnection()) {
+ fail("Initialization should have failed");
+ }
+ catch (PoolInitializationException e) {
+ // passed
+ }
+ }
+
+ @Test
+ public void testInvalidConnectionTestQuery()
+ {
+ class BadConnection extends StubConnection {
+ /** {@inheritDoc} */
+ @Override
+ public Statement createStatement() throws SQLException
+ {
+ throw new SQLException("Simulated exception in createStatement()");
+ }
+ }
+
+ StubDataSource stubDataSource = new StubDataSource() {
+ /** {@inheritDoc} */
+ @Override
+ public Connection getConnection() throws SQLException
+ {
+ return new BadConnection();
+ }
+ };
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(2);
+ config.setConnectionTimeout(TimeUnit.SECONDS.toMillis(3));
+ config.setConnectionTestQuery("VALUES 1");
+ config.setInitializationFailTimeout(TimeUnit.SECONDS.toMillis(2));
+ config.setDataSource(stubDataSource);
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ try (Connection c = ds.getConnection()) {
+ fail("getConnection() should have failed");
+ }
+ catch (SQLException e) {
+ assertSame("Simulated exception in createStatement()", e.getNextException().getMessage());
+ }
+ }
+ catch (PoolInitializationException e) {
+ assertSame("Simulated exception in createStatement()", e.getCause().getMessage());
+ }
+
+ config.setInitializationFailTimeout(0);
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ fail("Initialization should have failed");
+ }
+ catch (PoolInitializationException e) {
+ // passed
+ }
+ }
+
+ @Test
+ public void testPopulationSlowAcquisition() throws InterruptedException, SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMaximumPoolSize(20);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "1000");
+
+ StubConnection.slowCreate = true;
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ System.clearProperty("com.zaxxer.hikari.housekeeping.periodMs");
+
+ ds.setIdleTimeout(3000);
+
+ SECONDS.sleep(2);
+
+ HikariPool pool = getPool(ds);
+ assertSame("Total connections not as expected", 2, pool.getTotalConnections());
+ assertSame("Idle connections not as expected", 2, pool.getIdleConnections());
+
+ try (Connection connection = ds.getConnection()) {
+ assertNotNull(connection);
+
+ SECONDS.sleep(20);
+
+ assertSame("Second total connections not as expected", 20, pool.getTotalConnections());
+ assertSame("Second idle connections not as expected", 19, pool.getIdleConnections());
+ }
+
+ assertSame("Idle connections not as expected", 20, pool.getIdleConnections());
+
+ SECONDS.sleep(5);
+
+ assertSame("Third total connections not as expected", 20, pool.getTotalConnections());
+ assertSame("Third idle connections not as expected", 20, pool.getIdleConnections());
+ }
+ finally {
+ StubConnection.slowCreate = false;
+ }
+ }
+
+ @Test
+ public void testMinimumIdleZero() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(5);
+ config.setConnectionTimeout(1000L);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config);
+ Connection connection = ds.getConnection()) {
+ // passed
+ }
+ catch (SQLTransientConnectionException sqle) {
+ fail("Failed to obtain connection");
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestElf.java b/src/test/java/com/zaxxer/hikari/pool/TestElf.java
new file mode 100644
index 0000000..6438b11
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestElf.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.sql.Connection;
+import java.util.HashMap;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.layout.CsvLogEventLayout;
+import org.apache.logging.slf4j.Log4jLogger;
+import org.slf4j.LoggerFactory;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+/**
+ * Utility methods for testing.
+ *
+ * @author Brett Wooldridge
+ */
+public final class TestElf
+{
+ private TestElf() {
+ // default constructor
+ }
+
+ public static HikariPool getPool(HikariDataSource ds)
+ {
+ try {
+ Field field = ds.getClass().getDeclaredField("pool");
+ field.setAccessible(true);
+ return (HikariPool) field.get(ds);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static HashMap<Object, HikariPool> getMultiPool(HikariDataSource ds)
+ {
+ try {
+ Field field = ds.getClass().getDeclaredField("multiPool");
+ field.setAccessible(true);
+ return (HashMap<Object, HikariPool>) field.get(ds);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static boolean getConnectionCommitDirtyState(Connection connection)
+ {
+ try {
+ Field field = ProxyConnection.class.getDeclaredField("isCommitStateDirty");
+ field.setAccessible(true);
+ return field.getBoolean(connection);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void setConfigUnitTest(boolean unitTest)
+ {
+ try {
+ Field field = HikariConfig.class.getDeclaredField("unitTest");
+ field.setAccessible(true);
+ field.setBoolean(null, unitTest);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void setSlf4jTargetStream(Class<?> clazz, PrintStream stream)
+ {
+ try {
+ Log4jLogger log4Jlogger = (Log4jLogger) LoggerFactory.getLogger(clazz);
+
+ Field field = clazz.getClassLoader().loadClass("org.apache.logging.slf4j.Log4jLogger").getDeclaredField("logger");
+ field.setAccessible(true);
+
+ Logger logger = (Logger) field.get(log4Jlogger);
+ if (logger.getAppenders().containsKey("string")) {
+ Appender appender = logger.getAppenders().get("string");
+ logger.removeAppender(appender);
+ }
+
+ logger.addAppender(new StringAppender("string", stream));
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void setSlf4jLogLevel(Class<?> clazz, Level logLevel)
+ {
+ try {
+ Log4jLogger log4Jlogger = (Log4jLogger) LoggerFactory.getLogger(clazz);
+
+ Field field = clazz.getClassLoader().loadClass("org.apache.logging.slf4j.Log4jLogger").getDeclaredField("logger");
+ field.setAccessible(true);
+
+ Logger logger = (Logger) field.get(log4Jlogger);
+ logger.setLevel(logLevel);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static HikariConfig newHikariConfig()
+ {
+ final StackTraceElement callerStackTrace = Thread.currentThread().getStackTrace()[2];
+
+ String poolName = callerStackTrace.getMethodName();
+ if ("setup".equals(poolName)) {
+ poolName = callerStackTrace.getClassName();
+ }
+
+ final HikariConfig config = new HikariConfig();
+ config.setPoolName(poolName);
+ return config;
+ }
+
+ public static HikariDataSource newHikariDataSource()
+ {
+ final StackTraceElement callerStackTrace = Thread.currentThread().getStackTrace()[2];
+
+ String poolName = callerStackTrace.getMethodName();
+ if ("setup".equals(poolName)) {
+ poolName = callerStackTrace.getClassName();
+ }
+
+ final HikariDataSource ds = new HikariDataSource();
+ ds.setPoolName(poolName);
+ return ds;
+ }
+
+ private static class StringAppender extends AbstractAppender
+ {
+ private PrintStream stream;
+
+ StringAppender(String name, PrintStream stream)
+ {
+ super(name, null, CsvLogEventLayout.createDefaultLayout());
+ this.stream = stream;
+ }
+
+ @Override
+ public void append(LogEvent event)
+ {
+ stream.println(event.getMessage().getFormattedMessage());
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestHibernate.java b/src/test/java/com/zaxxer/hikari/pool/TestHibernate.java
new file mode 100644
index 0000000..c1d6bae
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestHibernate.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.util.Properties;
+
+import org.hibernate.service.UnknownUnwrapTypeException;
+import org.junit.Test;
+
+import com.zaxxer.hikari.hibernate.HikariConnectionProvider;
+
+public class TestHibernate
+{
+ @Test
+ public void testConnectionProvider() throws Exception
+ {
+ HikariConnectionProvider provider = new HikariConnectionProvider();
+
+ Properties props = new Properties();
+ props.load(getClass().getResourceAsStream("/hibernate.properties"));
+
+ provider.configure(props);
+ Connection connection = provider.getConnection();
+ provider.closeConnection(connection);
+
+ assertNotNull(provider.unwrap(HikariConnectionProvider.class));
+ assertFalse(provider.supportsAggressiveRelease());
+
+ try {
+ provider.unwrap(TestHibernate.class);
+ fail("Expected exception");
+ }
+ catch (UnknownUnwrapTypeException e) {
+ }
+
+ provider.stop();
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestJNDI.java b/src/test/java/com/zaxxer/hikari/pool/TestJNDI.java
new file mode 100644
index 0000000..b5cd40c
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestJNDI.java
@@ -0,0 +1,146 @@
+/*
+ * 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.pool;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+
+import org.junit.Test;
+import org.osjava.sj.jndi.AbstractContext;
+
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.HikariJNDIFactory;
+import com.zaxxer.hikari.mocks.StubDataSource;
+
+public class TestJNDI
+{
+ @Test
+ public void testJndiLookup1() throws Exception
+ {
+ HikariJNDIFactory jndi = new HikariJNDIFactory();
+ Reference ref = new Reference("javax.sql.DataSource");
+ ref.add(new BogusRef("driverClassName", "com.zaxxer.hikari.mocks.StubDriver"));
+ ref.add(new BogusRef("jdbcUrl", "jdbc:stub"));
+ ref.add(new BogusRef("username", "foo"));
+ ref.add(new BogusRef("password", "foo"));
+ ref.add(new BogusRef("minimumIdle", "0"));
+ ref.add(new BogusRef("maxLifetime", "30000"));
+ ref.add(new BogusRef("maximumPoolSize", "10"));
+ ref.add(new BogusRef("dataSource.loginTimeout", "10"));
+ Context nameCtx = new BogusContext();
+
+ try (HikariDataSource ds = (HikariDataSource) jndi.getObjectInstance(ref, null, nameCtx, null)) {
+ assertNotNull(ds);
+ assertEquals("foo", ds.getUsername());
+ }
+ }
+
+ @Test
+ public void testJndiLookup2() throws Exception
+ {
+ HikariJNDIFactory jndi = new HikariJNDIFactory();
+ Reference ref = new Reference("javax.sql.DataSource");
+ ref.add(new BogusRef("dataSourceJNDI", "java:comp/env/HikariDS"));
+ ref.add(new BogusRef("driverClassName", "com.zaxxer.hikari.mocks.StubDriver"));
+ ref.add(new BogusRef("jdbcUrl", "jdbc:stub"));
+ ref.add(new BogusRef("username", "foo"));
+ ref.add(new BogusRef("password", "foo"));
+ ref.add(new BogusRef("minimumIdle", "0"));
+ ref.add(new BogusRef("maxLifetime", "30000"));
+ ref.add(new BogusRef("maximumPoolSize", "10"));
+ ref.add(new BogusRef("dataSource.loginTimeout", "10"));
+ Context nameCtx = new BogusContext2();
+
+ try (HikariDataSource ds = (HikariDataSource) jndi.getObjectInstance(ref, null, nameCtx, null)) {
+ assertNotNull(ds);
+ assertEquals("foo", ds.getUsername());
+ }
+ }
+
+ @Test
+ public void testJndiLookup3() throws Exception
+ {
+ HikariJNDIFactory jndi = new HikariJNDIFactory();
+
+ Reference ref = new Reference("javax.sql.DataSource");
+ ref.add(new BogusRef("dataSourceJNDI", "java:comp/env/HikariDS"));
+ try {
+ jndi.getObjectInstance(ref, null, null, null);
+ fail();
+ }
+ catch (RuntimeException e) {
+ assertTrue(e.getMessage().contains("JNDI context does not found"));
+ }
+ }
+
+ private class BogusContext extends AbstractContext
+ {
+ @Override
+ public Context createSubcontext(Name name) throws NamingException
+ {
+ return null;
+ }
+
+ @Override
+ public Object lookup(String name) throws NamingException
+ {
+ final HikariDataSource ds = new HikariDataSource();
+ ds.setPoolName("TestJNDI");
+ return ds;
+ }
+ }
+
+ private class BogusContext2 extends AbstractContext
+ {
+ @Override
+ public Context createSubcontext(Name name) throws NamingException
+ {
+ return null;
+ }
+
+ @Override
+ public Object lookup(String name) throws NamingException
+ {
+ return new StubDataSource();
+ }
+ }
+
+ private class BogusRef extends RefAddr
+ {
+ private static final long serialVersionUID = 1L;
+
+ private String content;
+ BogusRef(String type, String content)
+ {
+ super(type);
+ this.content = content;
+ }
+
+ @Override
+ public Object getContent()
+ {
+ return content;
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestMBean.java b/src/test/java/com/zaxxer/hikari/pool/TestMBean.java
new file mode 100644
index 0000000..55ba733
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestMBean.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+
+import java.sql.SQLException;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+public class TestMBean
+{
+ @Test
+ public void testMBeanRegistration() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setRegisterMbeans(true);
+ config.setConnectionTimeout(2800);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ // Close immediately
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestMetrics.java b/src/test/java/com/zaxxer/hikari/pool/TestMetrics.java
new file mode 100644
index 0000000..fcba58e
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestMetrics.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
+import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.health.HealthCheck.Result;
+import com.codahale.metrics.health.HealthCheckRegistry;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
+import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory;
+import com.zaxxer.hikari.util.UtilityElf;
+
+import shaded.org.codehaus.plexus.interpolation.os.Os;
+
+/**
+ * Test HikariCP/CodaHale metrics integration.
+ *
+ * @author Brett Wooldridge
+ */
+public class TestMetrics
+{
+ @Test
+ public void testMetricWait() throws SQLException
+ {
+ MetricRegistry metricRegistry = new MetricRegistry();
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setMetricRegistry(metricRegistry);
+ config.setInitializationFailTimeout(Long.MAX_VALUE);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ ds.getConnection().close();
+
+ Timer timer = metricRegistry.getTimers(new MetricFilter() {
+ /** {@inheritDoc} */
+ @Override
+ public boolean matches(String name, Metric metric)
+ {
+ return "testMetricWait.pool.Wait".equals(MetricRegistry.name("testMetricWait", "pool", "Wait"));
+ }
+ }).values().iterator().next();
+
+ assertEquals(1, timer.getCount());
+ assertTrue(timer.getMeanRate() > 0.0);
+ }
+ }
+
+ @Test
+ public void testMetricUsage() throws SQLException
+ {
+ assumeFalse(Os.isFamily(Os.FAMILY_WINDOWS));
+ MetricRegistry metricRegistry = new MetricRegistry();
+
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setMetricRegistry(metricRegistry);
+ config.setInitializationFailTimeout(0);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ try (Connection connection = ds.getConnection()) {
+ UtilityElf.quietlySleep(250L);
+ }
+
+ Histogram histo = metricRegistry.getHistograms(new MetricFilter() {
+ /** {@inheritDoc} */
+ @Override
+ public boolean matches(String name, Metric metric)
+ {
+ return name.equals(MetricRegistry.name("testMetricUsage", "pool", "Usage"));
+ }
+ }).values().iterator().next();
+
+ assertEquals(1, histo.getCount());
+ double seventyFifth = histo.getSnapshot().get75thPercentile();
+ assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
+ }
+ }
+
+ @Test
+ public void testHealthChecks() throws Exception
+ {
+ MetricRegistry metricRegistry = new MetricRegistry();
+ HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
+
+ HikariConfig config = newHikariConfig();
+ config.setMaximumPoolSize(10);
+ config.setMetricRegistry(metricRegistry);
+ config.setHealthCheckRegistry(healthRegistry);
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.addHealthCheckProperty("connectivityCheckTimeoutMs", "1000");
+ config.addHealthCheckProperty("expected99thPercentileMs", "100");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ quietlySleep(TimeUnit.SECONDS.toMillis(2));
+
+ try (Connection connection = ds.getConnection()) {
+ // close immediately
+ }
+
+ try (Connection connection = ds.getConnection()) {
+ // close immediately
+ }
+
+ SortedMap<String, Result> healthChecks = healthRegistry.runHealthChecks();
+
+ Result connectivityResult = healthChecks.get("testHealthChecks.pool.ConnectivityCheck");
+ assertTrue(connectivityResult.isHealthy());
+
+ Result slaResult = healthChecks.get("testHealthChecks.pool.Connection99Percent");
+ assertTrue(slaResult.isHealthy());
+ }
+ }
+
+ @Test
+ public void testSetters1() throws Exception
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMaximumPoolSize(1);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ MetricRegistry metricRegistry = new MetricRegistry();
+ HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
+
+ try {
+ try (Connection connection = ds.getConnection()) {
+ // close immediately
+ }
+
+ // After the pool as started, we can only set them once...
+ ds.setMetricRegistry(metricRegistry);
+ ds.setHealthCheckRegistry(healthRegistry);
+
+ // and never again...
+ ds.setMetricRegistry(metricRegistry);
+ fail("Should not have been allowed to set registry after pool started");
+ }
+ catch (IllegalStateException ise) {
+ // pass
+ try {
+ ds.setHealthCheckRegistry(healthRegistry);
+ fail("Should not have been allowed to set registry after pool started");
+ }
+ catch (IllegalStateException ise2) {
+ // pass
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testSetters2() throws Exception
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMaximumPoolSize(1);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ MetricRegistry metricRegistry = new MetricRegistry();
+ HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
+
+ ds.setMetricRegistry(metricRegistry);
+ ds.setHealthCheckRegistry(healthRegistry);
+
+ // before the pool is started, we can set it any number of times...
+ ds.setMetricRegistry(metricRegistry);
+ ds.setHealthCheckRegistry(healthRegistry);
+
+ try (Connection connection = ds.getConnection()) {
+
+ // after the pool is started, we cannot set it any more
+ ds.setMetricRegistry(metricRegistry);
+ fail("Should not have been allowed to set registry after pool started");
+ }
+ catch (IllegalStateException ise) {
+ // pass
+ }
+ }
+ }
+
+ @Test
+ public void testSetters3() throws Exception
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMaximumPoolSize(1);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ MetricRegistry metricRegistry = new MetricRegistry();
+ MetricsTrackerFactory metricsTrackerFactory = new CodahaleMetricsTrackerFactory(metricRegistry);
+
+ try (Connection connection = ds.getConnection()) {
+
+ // After the pool as started, we can only set them once...
+ ds.setMetricsTrackerFactory(metricsTrackerFactory);
+
+ // and never again...
+ ds.setMetricsTrackerFactory(metricsTrackerFactory);
+ fail("Should not have been allowed to set metricsTrackerFactory after pool started");
+ }
+ catch (IllegalStateException ise) {
+ // pass
+ try {
+ // and never again... (even when calling another method)
+ ds.setMetricRegistry(metricRegistry);
+ fail("Should not have been allowed to set registry after pool started");
+ }
+ catch (IllegalStateException ise2) {
+ // pass
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testSetters4() throws Exception
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMaximumPoolSize(1);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ MetricRegistry metricRegistry = new MetricRegistry();
+
+ // before the pool is started, we can set it any number of times using either setter
+ ds.setMetricRegistry(metricRegistry);
+ ds.setMetricRegistry(metricRegistry);
+ ds.setMetricRegistry(metricRegistry);
+
+ try (Connection connection = ds.getConnection()) {
+
+ // after the pool is started, we cannot set it any more
+ ds.setMetricRegistry(metricRegistry);
+ fail("Should not have been allowed to set registry after pool started");
+ }
+ catch (IllegalStateException ise) {
+ // pass
+ }
+ }
+ }
+
+ @Test
+ public void testSetters5() throws Exception
+ {
+ try (HikariDataSource ds = newHikariDataSource()) {
+ ds.setMaximumPoolSize(1);
+ ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ MetricRegistry metricRegistry = new MetricRegistry();
+ MetricsTrackerFactory metricsTrackerFactory = new CodahaleMetricsTrackerFactory(metricRegistry);
+
+ // before the pool is started, we can set it any number of times using either setter
+ ds.setMetricsTrackerFactory(metricsTrackerFactory);
+ ds.setMetricsTrackerFactory(metricsTrackerFactory);
+ ds.setMetricsTrackerFactory(metricsTrackerFactory);
+
+ try (Connection connection = ds.getConnection()) {
+
+ // after the pool is started, we cannot set it any more
+ ds.setMetricsTrackerFactory(metricsTrackerFactory);
+ fail("Should not have been allowed to set registry factory after pool started");
+ }
+ catch (IllegalStateException ise) {
+ // pass
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestPropertySetter.java b/src/test/java/com/zaxxer/hikari/pool/TestPropertySetter.java
new file mode 100644
index 0000000..fb10446
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestPropertySetter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.sql.DataSource;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.util.PropertyElf;
+
+public class TestPropertySetter
+{
+ @Test
+ public void testProperty1() throws Exception
+ {
+ Properties propfile1 = new Properties();
+ propfile1.load(TestPropertySetter.class.getResourceAsStream("/propfile1.properties"));
+ HikariConfig config = new HikariConfig(propfile1);
+ config.validate();
+
+ assertEquals(5, config.getMinimumIdle());
+ assertEquals("SELECT 1", config.getConnectionTestQuery());
+ }
+
+ @Test
+ public void testProperty2() throws Exception
+ {
+ Properties propfile2 = new Properties();
+ propfile2.load(TestPropertySetter.class.getResourceAsStream("/propfile2.properties"));
+ HikariConfig config = new HikariConfig(propfile2);
+ config.validate();
+
+ Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
+ DataSource dataSource = (DataSource) clazz.newInstance();
+ PropertyElf.setTargetFromProperties(dataSource, config.getDataSourceProperties());
+ }
+
+ @Test
+ public void testObjectProperty() throws Exception
+ {
+ HikariConfig config = newHikariConfig();
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ PrintWriter writer = new PrintWriter(new ByteArrayOutputStream());
+ config.addDataSourceProperty("logWriter", writer);
+
+ Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
+ DataSource dataSource = (DataSource) clazz.newInstance();
+ PropertyElf.setTargetFromProperties(dataSource, config.getDataSourceProperties());
+
+ assertSame(PrintWriter.class, dataSource.getLogWriter().getClass());
+ }
+
+ @Test
+ public void testPropertyUpperCase() throws Exception
+ {
+ Properties propfile3 = new Properties();
+ propfile3.load(TestPropertySetter.class.getResourceAsStream("/propfile3.properties"));
+ HikariConfig config = new HikariConfig(propfile3);
+ config.validate();
+
+ Class<?> clazz = this.getClass().getClassLoader().loadClass(config.getDataSourceClassName());
+ DataSource dataSource = (DataSource) clazz.newInstance();
+ PropertyElf.setTargetFromProperties(dataSource, config.getDataSourceProperties());
+ }
+
+ @Test
+ public void testGetPropertyNames() throws Exception
+ {
+ Set<String> propertyNames = PropertyElf.getPropertyNames(HikariConfig.class);
+ assertTrue(propertyNames.contains("dataSourceClassName"));
+ }
+
+ @Test
+ public void testSetNonExistantPropertyName() throws Exception
+ {
+ try {
+ Properties props = new Properties();
+ props.put("what", "happened");
+ PropertyElf.setTargetFromProperties(new HikariConfig(), props);
+ fail();
+ }
+ catch (RuntimeException e) {
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestProxies.java b/src/test/java/com/zaxxer/hikari/pool/TestProxies.java
new file mode 100644
index 0000000..96f4066
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestProxies.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubConnection;
+import com.zaxxer.hikari.mocks.StubStatement;
+
+public class TestProxies
+{
+ @Test
+ public void testProxyCreation() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ Connection conn = ds.getConnection();
+
+ assertNotNull(conn.createStatement(ResultSet.FETCH_FORWARD, ResultSet.TYPE_SCROLL_INSENSITIVE));
+ assertNotNull(conn.createStatement(ResultSet.FETCH_FORWARD, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.HOLD_CURSORS_OVER_COMMIT));
+ assertNotNull(conn.prepareCall("some sql"));
+ assertNotNull(conn.prepareCall("some sql", ResultSet.FETCH_FORWARD, ResultSet.TYPE_SCROLL_INSENSITIVE));
+ assertNotNull(conn.prepareCall("some sql", ResultSet.FETCH_FORWARD, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.HOLD_CURSORS_OVER_COMMIT));
+ assertNotNull(conn.prepareStatement("some sql", PreparedStatement.NO_GENERATED_KEYS));
+ assertNotNull(conn.prepareStatement("some sql", new int[3]));
+ assertNotNull(conn.prepareStatement("some sql", new String[3]));
+ assertNotNull(conn.prepareStatement("some sql", ResultSet.FETCH_FORWARD, ResultSet.TYPE_SCROLL_INSENSITIVE));
+ assertNotNull(conn.prepareStatement("some sql", ResultSet.FETCH_FORWARD, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.HOLD_CURSORS_OVER_COMMIT));
+ assertNotNull(conn.toString());
+
+ assertTrue(conn.isWrapperFor(Connection.class));
+ assertTrue(conn.isValid(10));
+ assertFalse(conn.isClosed());
+ assertTrue(conn.unwrap(StubConnection.class) instanceof StubConnection);
+ try {
+ conn.unwrap(TestProxies.class);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+ }
+ }
+
+ @Test
+ public void testStatementProxy() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ Connection conn = ds.getConnection();
+
+ PreparedStatement stmt = conn.prepareStatement("some sql");
+ stmt.executeQuery();
+ stmt.executeQuery("some sql");
+ assertFalse(stmt.isClosed());
+ assertNotNull(stmt.getGeneratedKeys());
+ assertNotNull(stmt.getResultSet());
+ assertNotNull(stmt.getConnection());
+ assertTrue(stmt.unwrap(StubStatement.class) instanceof StubStatement);
+ try {
+ stmt.unwrap(TestProxies.class);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+ }
+ }
+
+ @Test
+ public void testStatementExceptions() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTimeout(TimeUnit.SECONDS.toMillis(1));
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ Connection conn = ds.getConnection();
+ StubConnection stubConnection = conn.unwrap(StubConnection.class);
+ stubConnection.throwException = true;
+
+ try {
+ conn.createStatement();
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.createStatement(0, 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.createStatement(0, 0, 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareCall("");
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareCall("", 0, 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareCall("", 0, 0, 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareStatement("");
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareStatement("", 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareStatement("", new int[0]);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareStatement("", new String[0]);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareStatement("", 0, 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.prepareStatement("", 0, 0, 0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+ }
+ }
+
+ @Test
+ public void testOtherExceptions() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(0);
+ config.setMaximumPoolSize(1);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ try (Connection conn = ds.getConnection()) {
+ StubConnection stubConnection = conn.unwrap(StubConnection.class);
+ stubConnection.throwException = true;
+
+ try {
+ conn.setTransactionIsolation(Connection.TRANSACTION_NONE);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.isReadOnly();
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.setReadOnly(false);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.setCatalog("");
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.setAutoCommit(false);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.clearWarnings();
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.isValid(0);
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.isWrapperFor(getClass());
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.unwrap(getClass());
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ conn.close();
+ fail();
+ }
+ catch (SQLException e) {
+ // pass
+ }
+
+ try {
+ assertFalse(conn.isValid(0));
+ }
+ catch (SQLException e) {
+ fail();
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/TestValidation.java b/src/test/java/com/zaxxer/hikari/pool/TestValidation.java
new file mode 100644
index 0000000..46ba779
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/TestValidation.java
@@ -0,0 +1,253 @@
+/*
+ * 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.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.setSlf4jTargetStream;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+
+/**
+ * @author Brett Wooldridge
+ */
+public class TestValidation
+{
+ @Test
+ public void validateLoadProperties()
+ {
+ System.setProperty("hikaricp.configurationFile", "/propfile1.properties");
+ HikariConfig config = newHikariConfig();
+ System.clearProperty("hikaricp.configurationFile");
+ assertEquals(5, config.getMinimumIdle());
+ }
+
+ @Test
+ public void validateMissingProperties()
+ {
+ try {
+ HikariConfig config = new HikariConfig("missing");
+ config.validate();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("property file"));
+ }
+ }
+
+ @Test
+ public void validateMissingDS()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.validate();
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("dataSource or dataSourceClassName or jdbcUrl is required."));
+ }
+ }
+
+ @Test
+ public void validateMissingUrl()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setDriverClassName("com.zaxxer.hikari.mocks.StubDriver");
+ config.validate();
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("jdbcUrl is required with driverClassName"));
+ }
+ }
+
+ @Test
+ public void validateDriverAndUrl()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setDriverClassName("com.zaxxer.hikari.mocks.StubDriver");
+ config.setJdbcUrl("jdbc:stub");
+ config.validate();
+ }
+ catch (Throwable t) {
+ fail(t.getMessage());
+ }
+ }
+
+ @Test
+ public void validateBadDriver()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setDriverClassName("invalid");
+ config.validate();
+ fail();
+ }
+ catch (RuntimeException ise) {
+ assertTrue(ise.getMessage().contains("class of driverClassName "));
+ }
+ }
+
+ @Test
+ public void validateInvalidConnectionTimeout()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setConnectionTimeout(10L);
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("connectionTimeout cannot be less than 250ms"));
+ }
+ }
+
+ @Test
+ public void validateInvalidValidationTimeout()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setValidationTimeout(10L);
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("validationTimeout cannot be less than 250ms"));
+ }
+ }
+
+ @Test
+ public void validateInvalidIdleTimeout()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setIdleTimeout(-1L);
+ fail("negative idle timeout accepted");
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("idleTimeout cannot be negative"));
+ }
+ }
+
+ @Test
+ public void validateIdleTimeoutTooSmall()
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos, true);
+ setSlf4jTargetStream(HikariConfig.class, ps);
+
+ HikariConfig config = newHikariConfig();
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.setIdleTimeout(TimeUnit.SECONDS.toMillis(5));
+ config.validate();
+ assertTrue(new String(baos.toByteArray()).contains("less than 10000ms"));
+ }
+
+ @Test
+ public void validateIdleTimeoutExceedsLifetime()
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos, true);
+ setSlf4jTargetStream(HikariConfig.class, ps);
+
+ HikariConfig config = newHikariConfig();
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+ config.setMaxLifetime(TimeUnit.MINUTES.toMillis(2));
+ config.setIdleTimeout(TimeUnit.MINUTES.toMillis(3));
+ config.validate();
+
+ String s = new String(baos.toByteArray());
+ assertTrue("idleTimeout is close to or more than maxLifetime, disabling it." + s + "*", s.contains("is close to or more than maxLifetime"));
+ }
+
+ @Test
+ public void validateInvalidMinIdle()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(-1);
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("minimumIdle cannot be negative"));
+ }
+ }
+
+ @Test
+ public void validateInvalidMaxPoolSize()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setMaximumPoolSize(0);
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ assertTrue(ise.getMessage().contains("maxPoolSize cannot be less than 1"));
+ }
+ }
+
+ @Test
+ public void validateInvalidLifetime()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setConnectionTimeout(Integer.MAX_VALUE);
+ config.setIdleTimeout(1000L);
+ config.setMaxLifetime(-1L);
+ config.setLeakDetectionThreshold(1000L);
+ config.validate();
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ // pass
+ }
+ }
+
+ @Test
+ public void validateInvalidLeakDetection()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setLeakDetectionThreshold(1000L);
+ config.validate();
+ fail();
+ }
+ catch (IllegalArgumentException ise) {
+ // pass
+ }
+ }
+
+ @Test
+ public void validateZeroConnectionTimeout()
+ {
+ try {
+ HikariConfig config = newHikariConfig();
+ config.setConnectionTimeout(0);
+ config.validate();
+ assertEquals(Integer.MAX_VALUE, config.getConnectionTimeout());
+ }
+ catch (IllegalArgumentException ise) {
+ // pass
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/pool/UnwrapTest.java b/src/test/java/com/zaxxer/hikari/pool/UnwrapTest.java
new file mode 100644
index 0000000..d13c8f4
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/pool/UnwrapTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.pool;
+
+import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
+import static com.zaxxer.hikari.pool.TestElf.getPool;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import com.zaxxer.hikari.mocks.StubConnection;
+import com.zaxxer.hikari.mocks.StubDataSource;
+
+/**
+ * @author Brett Wooldridge
+ */
+public class UnwrapTest
+{
+ @Test
+ public void testUnwrapConnection() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ ds.getConnection().close();
+ assertSame("Idle connections not as expected", 1, getPool(ds).getIdleConnections());
+
+ Connection connection = ds.getConnection();
+ assertNotNull(connection);
+
+ StubConnection unwrapped = connection.unwrap(StubConnection.class);
+ assertTrue("unwrapped connection is not instance of StubConnection: " + unwrapped, (unwrapped != null && unwrapped instanceof StubConnection));
+ }
+ }
+
+ @Test
+ public void testUnwrapDataSource() throws SQLException
+ {
+ HikariConfig config = newHikariConfig();
+ config.setMinimumIdle(1);
+ config.setMaximumPoolSize(1);
+ config.setInitializationFailTimeout(0);
+ config.setConnectionTestQuery("VALUES 1");
+ config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
+
+ try (HikariDataSource ds = new HikariDataSource(config)) {
+ StubDataSource unwrap = ds.unwrap(StubDataSource.class);
+ assertNotNull(unwrap);
+ assertTrue(unwrap instanceof StubDataSource);
+
+ assertTrue(ds.isWrapperFor(HikariDataSource.class));
+ assertTrue(ds.unwrap(HikariDataSource.class) instanceof HikariDataSource);
+
+ assertFalse(ds.isWrapperFor(getClass()));
+ try {
+ ds.unwrap(getClass());
+ }
+ catch (SQLException e) {
+ assertTrue(e.getMessage().contains("Wrapped DataSource"));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/util/ClockSourceTest.java b/src/test/java/com/zaxxer/hikari/util/ClockSourceTest.java
new file mode 100644
index 0000000..2d0482a
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/util/ClockSourceTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+/**
+ *
+ * @author Brett Wooldridge
+ */
+public class ClockSourceTest
+{
+ @Test
+ public void testClockSourceDisplay()
+ {
+ ClockSource msSource = new ClockSource.MillisecondClockSource();
+
+ final long sTime = DAYS.toMillis(3) + HOURS.toMillis(9) + MINUTES.toMillis(24) + SECONDS.toMillis(18) + MILLISECONDS.toMillis(572);
+
+ final long eTime = DAYS.toMillis(4) + HOURS.toMillis(9) + MINUTES.toMillis(55) + SECONDS.toMillis(23) + MILLISECONDS.toMillis(777);
+ String ds1 = msSource.elapsedDisplayString0(sTime, eTime);
+ Assert.assertEquals("1d31m5s205ms", ds1);
+
+ final long eTime2 = DAYS.toMillis(3) + HOURS.toMillis(8) + MINUTES.toMillis(24) + SECONDS.toMillis(23) + MILLISECONDS.toMillis(777);
+ String ds2 = msSource.elapsedDisplayString0(sTime, eTime2);
+ Assert.assertEquals("-59m54s795ms", ds2);
+
+
+ ClockSource nsSource = new ClockSource.NanosecondClockSource();
+
+ final long sTime2 = DAYS.toNanos(3) + HOURS.toNanos(9) + MINUTES.toNanos(24) + SECONDS.toNanos(18) + MILLISECONDS.toNanos(572) + MICROSECONDS.toNanos(324) + NANOSECONDS.toNanos(823);
+
+ final long eTime3 = DAYS.toNanos(4) + HOURS.toNanos(19) + MINUTES.toNanos(55) + SECONDS.toNanos(23) + MILLISECONDS.toNanos(777) + MICROSECONDS.toNanos(0) + NANOSECONDS.toNanos(982);
+ String ds3 = nsSource.elapsedDisplayString0(sTime2, eTime3);
+ Assert.assertEquals("1d10h31m5s204ms676µs159ns", ds3);
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/util/TestFastList.java b/src/test/java/com/zaxxer/hikari/util/TestFastList.java
new file mode 100644
index 0000000..089ff5a
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/util/TestFastList.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013, 2014 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package com.zaxxer.hikari.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.junit.Test;
+
+import com.zaxxer.hikari.mocks.StubStatement;
+
+public class TestFastList
+{
+ @Test
+ public void testAddRemove()
+ {
+ ArrayList<Statement> verifyList = new ArrayList<>();
+
+ FastList<Statement> list = new FastList<>(Statement.class);
+ for (int i = 0; i < 32; i++)
+ {
+ StubStatement statement = new StubStatement(null);
+ list.add(statement);
+ verifyList.add(statement);
+ }
+
+ for (int i = 0; i < 32; i++)
+ {
+ assertNotNull("Element " + i + " was null but should be " + verifyList.get(i), list.get(0));
+ int size = list.size();
+ list.remove(verifyList.get(i));
+ assertSame(size - 1, list.size());
+ }
+ }
+
+ @Test
+ public void testAddRemoveTail()
+ {
+ ArrayList<Statement> verifyList = new ArrayList<>();
+
+ FastList<Statement> list = new FastList<>(Statement.class);
+ for (int i = 0; i < 32; i++)
+ {
+ StubStatement statement = new StubStatement(null);
+ list.add(statement);
+ verifyList.add(statement);
+ }
+
+ for (int i = 31; i >= 0; i--)
+ {
+ assertNotNull("Element " + i, list.get(i));
+ int size = list.size();
+ list.remove(verifyList.get(i));
+ assertSame(size - 1, list.size());
+ }
+ }
+
+ @Test
+ public void testOverflow()
+ {
+ ArrayList<Statement> verifyList = new ArrayList<>();
+
+ FastList<Statement> list = new FastList<>(Statement.class);
+ for (int i = 0; i < 100; i++)
+ {
+ StubStatement statement = new StubStatement(null);
+ list.add(statement);
+ verifyList.add(statement);
+ }
+
+ for (int i = 0; i < 100; i++)
+ {
+ assertNotNull("Element " + i, list.get(i));
+ assertSame(verifyList.get(i), list.get(i));
+ }
+ }
+
+ @Test
+ public void testIterator()
+ {
+ FastList<Statement> list = new FastList<>(Statement.class);
+ for (int i = 0; i < 100; i++)
+ {
+ StubStatement statement = new StubStatement(null);
+ list.add(statement);
+ }
+
+ Iterator<Statement> iter = list.iterator();
+ for (int i = 0; i < list.size(); i++) {
+ assertSame(list.get(i), iter.next());
+ }
+ }
+
+ @Test
+ public void testClear()
+ {
+ FastList<Statement> list = new FastList<>(Statement.class);
+ for (int i = 0; i < 100; i++)
+ {
+ StubStatement statement = new StubStatement(null);
+ list.add(statement);
+ }
+
+ assertNotEquals(0, list.size());
+ list.clear();
+ assertEquals(0, list.size());
+ }
+
+ @Test
+ public void testRemoveLast()
+ {
+ FastList<Statement> list = new FastList<>(Statement.class);
+
+ Statement last = null;
+ for (int i = 0; i < 100; i++)
+ {
+ StubStatement statement = new StubStatement(null);
+ list.add(statement);
+ last = statement;
+ }
+
+ assertEquals(last, list.removeLast());
+ assertEquals(99, list.size());
+ }
+
+ @Test
+ public void testPolyMorphism1()
+ {
+ class Foo implements Base2 {
+
+ }
+
+ class Bar extends Foo {
+
+ }
+
+ FastList<Base> list = new FastList<>(Base.class, 2);
+ list.add(new Foo());
+ list.add(new Foo());
+ list.add(new Bar());
+ }
+
+ interface Base
+ {
+
+ }
+
+ interface Base2 extends Base
+ {
+
+ }
+}
diff --git a/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java b/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java
new file mode 100644
index 0000000..06957b2
--- /dev/null
+++ b/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2017 Brett Wooldridge
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zaxxer.hikari.util;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.concurrent.CompletableFuture;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry;
+
+/**
+ * @author Brett Wooldridge
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class TomcatConcurrentBagLeakTest
+{
+ @Test
+ public void testConcurrentBagForLeaks() throws Exception
+ {
+ ClassLoader cl = new FauxWebClassLoader();
+ Class<?> clazz = cl.loadClass(this.getClass().getName() + "$FauxWebContext");
+ Object fauxWebContext = clazz.newInstance();
+
+ Method createConcurrentBag = clazz.getDeclaredMethod("createConcurrentBag");
+ createConcurrentBag.invoke(fauxWebContext);
+
+ Field failureException = clazz.getDeclaredField("failureException");
+ Exception ex = (Exception) failureException.get(fauxWebContext);
+ assertNull(ex);
+ }
+
+ @Test
+ public void testConcurrentBagForLeaks2() throws Exception
+ {
+ ClassLoader cl = this.getClass().getClassLoader();
+ Class<?> clazz = cl.loadClass(this.getClass().getName() + "$FauxWebContext");
+ Object fauxWebContext = clazz.newInstance();
+
+ Method createConcurrentBag = clazz.getDeclaredMethod("createConcurrentBag");
+ createConcurrentBag.invoke(fauxWebContext);
+
+ Field failureException = clazz.getDeclaredField("failureException");
+ Exception ex = (Exception) failureException.get(fauxWebContext);
+ assertNotNull(ex);
+ }
+
+ static class FauxWebClassLoader extends ClassLoader
+ {
+ static final byte[] classBytes = new byte[16_000];
+
+ @Override
+ public Class<?> loadClass(String name) throws ClassNotFoundException
+ {
+ if (name.startsWith("java") || name.startsWith("org")) {
+ return super.loadClass(name, true);
+ }
+
+ final String resourceName = "/" + name.replace('.', '/') + ".class";
+ final URL resource = this.getClass().getResource(resourceName);
+ try (DataInputStream is = new DataInputStream(resource.openStream())) {
+ int read = 0;
+ while (read < classBytes.length) {
+ final int rc = is.read(classBytes, read, classBytes.length - read);
+ if (rc == -1) {
+ break;
+ }
+ read += rc;
+ }
+
+ return defineClass(name, classBytes, 0, read);
+ }
+ catch (IOException e) {
+ throw new ClassNotFoundException(name);
+ }
+ }
+ }
+
+ public static class PoolEntry implements IConcurrentBagEntry
+ {
+ private int state;
+
+ @Override
+ public boolean compareAndSet(int expectState, int newState)
+ {
+ this.state = newState;
+ return true;
+ }
+
+ @Override
+ public void setState(int newState)
+ {
+ this.state = newState;
+ }
+
+ @Override
+ public int getState()
+ {
+ return state;
+ }
+ }
+
+ public static class FauxWebContext
+ {
+ private static final Logger log = LoggerFactory.getLogger(FauxWebContext.class);
+
+ public Exception failureException;
+
+ public void createConcurrentBag() throws InterruptedException
+ {
+ try (ConcurrentBag<PoolEntry> bag = new ConcurrentBag<>((x) -> CompletableFuture.completedFuture(Boolean.TRUE))) {
+
+ PoolEntry entry = new PoolEntry();
+ bag.add(entry);
+
+ PoolEntry borrowed = bag.borrow(100, MILLISECONDS);
+ bag.requite(borrowed);
+
+ PoolEntry removed = bag.borrow(100, MILLISECONDS);
+ bag.remove(removed);
+ }
+
+ checkThreadLocalsForLeaks();
+ }
+
+ private void checkThreadLocalsForLeaks()
+ {
+ Thread[] threads = getThreads();
+
+ try {
+ // Make the fields in the Thread class that store ThreadLocals
+ // accessible
+ Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
+ threadLocalsField.setAccessible(true);
+ Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
+ inheritableThreadLocalsField.setAccessible(true);
+ // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects
+ // accessible
+ Class<?> tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
+ Field tableField = tlmClass.getDeclaredField("table");
+ tableField.setAccessible(true);
+ Method expungeStaleEntriesMethod = tlmClass.getDeclaredMethod("expungeStaleEntries");
+ expungeStaleEntriesMethod.setAccessible(true);
+
+ for (int i = 0; i < threads.length; i++) {
+ Object threadLocalMap;
+ if (threads[i] != null) {
+
+ // Clear the first map
+ threadLocalMap = threadLocalsField.get(threads[i]);
+ if (null != threadLocalMap) {
+ expungeStaleEntriesMethod.invoke(threadLocalMap);
+ checkThreadLocalMapForLeaks(threadLocalMap, tableField);
+ }
+
+ // Clear the second map
+ threadLocalMap = inheritableThreadLocalsField.get(threads[i]);
+ if (null != threadLocalMap) {
+ expungeStaleEntriesMethod.invoke(threadLocalMap);
+ checkThreadLocalMapForLeaks(threadLocalMap, tableField);
+ }
+ }
+ }
+ }
+ catch (Throwable t) {
+ log.warn("Failed to check for ThreadLocal references for web application [{}]", t);
+ failureException = new Exception();
+ }
+ }
+
+ private Object getContextName()
+ {
+ return this.getClass().getName();
+ }
+
+ // THE FOLLOWING CODE COPIED FROM APACHE TOMCAT (2017/01/08)
+
+ /**
+ * Analyzes the given thread local map object. Also pass in the field that
+ * points to the internal table to save re-calculating it on every
+ * call to this method.
+ */
+ private void checkThreadLocalMapForLeaks(Object map, Field internalTableField) throws IllegalAccessException, NoSuchFieldException
+ {
+ if (map != null) {
+ Object[] table = (Object[]) internalTableField.get(map);
+ if (table != null) {
+ for (int j = 0; j < table.length; j++) {
+ Object obj = table[j];
+ if (obj != null) {
+ boolean keyLoadedByWebapp = false;
+ boolean valueLoadedByWebapp = false;
+ // Check the key
+ Object key = ((Reference<?>) obj).get();
+ if (this.equals(key) || loadedByThisOrChild(key)) {
+ keyLoadedByWebapp = true;
+ }
+ // Check the value
+ Field valueField = obj.getClass().getDeclaredField("value");
+ valueField.setAccessible(true);
+ Object value = valueField.get(obj);
+ if (this.equals(value) || loadedByThisOrChild(value)) {
+ valueLoadedByWebapp = true;
+ }
+ if (keyLoadedByWebapp || valueLoadedByWebapp) {
+ Object[] args = new Object[5];
+ args[0] = getContextName();
+ if (key != null) {
+ args[1] = getPrettyClassName(key.getClass());
+ try {
+ args[2] = key.toString();
+ }
+ catch (Exception e) {
+ log.warn("Unable to determine string representation of key of type [{}]", args[1], e);
+ args[2] = "Unknown";
+ }
+ }
+ if (value != null) {
+ args[3] = getPrettyClassName(value.getClass());
+ try {
+ args[4] = value.toString();
+ }
+ catch (Exception e) {
+ log.warn("webappClassLoader.checkThreadLocalsForLeaks.badValue {}", args[3], e);
+ args[4] = "Unknown";
+ }
+ }
+
+ if (valueLoadedByWebapp) {
+ log.error("The web application [{}] created a ThreadLocal with key " +
+ "(value [{}]) and a value of type [{}] (value [{}]) but failed to remove " +
+ "it when the web application was stopped. Threads are going to be renewed " +
+ "over time to try and avoid a probable memory leak.", args);
+ failureException = new Exception();
+ }
+ else if (value == null) {
+ log.debug("The web application [{}] created a ThreadLocal with key of type [{}] " +
+ "(value [{}]). The ThreadLocal has been correctly set to null and the " +
+ "key will be removed by GC.", args);
+ failureException = new Exception();
+ }
+ else {
+ log.debug("The web application [{}] created a ThreadLocal with key of type [{}] " +
+ "(value [{}]) and a value of type [{}] (value [{}]). Since keys are only " +
+ "weakly held by the ThreadLocal Map this is not a memory leak.", args);
+ failureException = new Exception();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean loadedByThisOrChild(Object key)
+ {
+ return key.getClass().getClassLoader() == this.getClass().getClassLoader();
+ }
+
+ /*
+ * Get the set of current threads as an array.
+ */
+ private Thread[] getThreads()
+ {
+ // Get the current thread group
+ ThreadGroup tg = Thread.currentThread().getThreadGroup();
+ // Find the root thread group
+ try {
+ while (tg.getParent() != null) {
+ tg = tg.getParent();
+ }
+ }
+ catch (SecurityException se) {
+ log.warn("Unable to obtain the parent for ThreadGroup [{}]. It will not be possible to check all threads for potential memory leaks", tg.getName(), se);
+ }
+
+ int threadCountGuess = tg.activeCount() + 50;
+ Thread[] threads = new Thread[threadCountGuess];
+ int threadCountActual = tg.enumerate(threads);
+ // Make sure we don't miss any threads
+ while (threadCountActual == threadCountGuess) {
+ threadCountGuess *= 2;
+ threads = new Thread[threadCountGuess];
+ // Note tg.enumerate(Thread[]) silently ignores any threads that
+ // can't fit into the array
+ threadCountActual = tg.enumerate(threads);
+ }
+
+ return threads;
+ }
+
+ private String getPrettyClassName(Class<?> clazz)
+ {
+ String name = clazz.getCanonicalName();
+ if (name == null) {
+ name = clazz.getName();
+ }
+ return name;
+ }
+ }
+}