summaryrefslogtreecommitdiff
path: root/src/test/java/com/zaxxer/hikari/pool
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/zaxxer/hikari/pool')
-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
26 files changed, 4446 insertions, 0 deletions
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"));
+ }
+ }
+ }
+}