View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.dbcp2;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNotSame;
25  import static org.junit.jupiter.api.Assertions.assertNull;
26  import static org.junit.jupiter.api.Assertions.assertSame;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  import static org.junit.jupiter.api.Assertions.fail;
30  
31  import java.sql.CallableStatement;
32  import java.sql.Connection;
33  import java.sql.PreparedStatement;
34  import java.sql.ResultSet;
35  import java.sql.SQLException;
36  import java.sql.Statement;
37  import java.time.Duration;
38  import java.util.Hashtable;
39  import java.util.Random;
40  import java.util.Stack;
41  
42  import org.junit.jupiter.api.AfterEach;
43  import org.junit.jupiter.api.Test;
44  
45  // XXX FIX ME XXX
46  // this class still needs some cleanup, but at least
47  // this consolidates most of the relevant test code
48  // in a fairly re-usable fashion
49  // XXX FIX ME XXX
50  
51  /**
52   * Base test suite for DBCP pools.
53   */
54  public abstract class TestConnectionPool {
55  
56      protected final class PoolTest implements Runnable {
57          /**
58           * The number of milliseconds to hold onto a database connection
59           */
60          private final Duration connHoldDuration;
61  
62          private final int numStatements;
63  
64          private volatile boolean isRun;
65  
66          private String state; // No need to be volatile if it is read after the thread finishes
67  
68          private final Thread thread;
69  
70          private Throwable thrown;
71  
72          private final Random random = new Random();
73  
74          // Debug for DBCP-318
75          private final long createdMillis; // When object was created
76          private long started; // when thread started
77          private long ended; // when thread ended
78          private long preconnected; // just before connect
79          private long connected; // when thread last connected
80          private long postconnected; // when thread released connection
81          private int loops;
82          private int connHash; // Connection identity hashCode (to see which one is reused)
83  
84          private final boolean stopOnException; // If true, don't rethrow Exception
85  
86          private final boolean loopOnce; // If true, don't repeat loop
87  
88          public PoolTest(final ThreadGroup threadGroup, final Duration connHoldDuration, final boolean isStopOnException) {
89              this(threadGroup, connHoldDuration, isStopOnException, false, 1);
90          }
91  
92          private PoolTest(final ThreadGroup threadGroup, final Duration connHoldDuration, final boolean isStopOnException, final boolean once,
93                  final int numStatements) {
94              this.loopOnce = once;
95              this.connHoldDuration = connHoldDuration;
96              stopOnException = isStopOnException;
97              isRun = true; // Must be done here so main thread is guaranteed to be able to set it false
98              thrown = null;
99              thread = new Thread(threadGroup, this, "Thread+" + currentThreadCount++);
100             thread.setDaemon(false);
101             createdMillis = timeStampMillis();
102             this.numStatements = numStatements;
103         }
104 
105         public PoolTest(final ThreadGroup threadGroup, final Duration connHoldDuration, final boolean isStopOnException, final int numStatements) {
106             this(threadGroup, connHoldDuration, isStopOnException, false, numStatements);
107         }
108 
109         public Thread getThread() {
110             return thread;
111         }
112 
113         @Override
114         public void run() {
115             started = timeStampMillis();
116             try {
117                 while (isRun) {
118                     loops++;
119                     state = "Getting Connection";
120                     preconnected = timeStampMillis();
121                     try (Connection conn = getConnection()) {
122                         connHash = System.identityHashCode(((DelegatingConnection<?>) conn).getInnermostDelegate());
123                         connected = timeStampMillis();
124                         state = "Using Connection";
125                         assertNotNull(conn);
126                         final String sql = numStatements == 1 ? "select * from dual" : "select count " + random.nextInt(numStatements - 1);
127                         try (PreparedStatement stmt = conn.prepareStatement(sql)) {
128                             assertNotNull(stmt);
129                             try (ResultSet rset = stmt.executeQuery()) {
130                                 assertNotNull(rset);
131                                 assertTrue(rset.next());
132                                 state = "Holding Connection";
133                                 Thread.sleep(connHoldDuration.toMillis());
134                                 state = "Closing ResultSet";
135                             }
136                             state = "Closing Statement";
137                         }
138                         state = "Closing Connection";
139                     }
140                     postconnected = timeStampMillis();
141                     state = "Closed";
142                     if (loopOnce) {
143                         break; // Or could set isRun=false
144                     }
145                 }
146                 state = DONE;
147             } catch (final Throwable t) {
148                 thrown = t;
149                 if (!stopOnException) {
150                     throw new RuntimeException();
151                 }
152             } finally {
153                 ended = timeStampMillis();
154             }
155         }
156 
157         public void start() {
158             thread.start();
159         }
160 
161         public void stop() {
162             isRun = false;
163         }
164     }
165 
166     final class TestThread implements Runnable {
167         final Random random = new Random();
168         boolean complete;
169         boolean failed;
170         int iter = 100;
171         int delay = 50;
172 
173         public TestThread() {
174         }
175 
176         public TestThread(final int iter) {
177             this.iter = iter;
178         }
179 
180         public TestThread(final int iter, final int delay) {
181             this.iter = iter;
182             this.delay = delay;
183         }
184 
185         public boolean complete() {
186             return complete;
187         }
188 
189         public boolean failed() {
190             return failed;
191         }
192 
193         @Override
194         public void run() {
195             for (int i = 0; i < iter; i++) {
196                 try {
197                     Thread.sleep(random.nextInt(delay));
198                 } catch (final Exception e) {
199                     // ignored
200                 }
201                 try (Connection conn = newConnection();
202                         PreparedStatement stmt = conn.prepareStatement("select 'literal', SYSDATE from dual");
203                         ResultSet rset = stmt.executeQuery()) {
204                     try {
205                         Thread.sleep(random.nextInt(delay));
206                     } catch (final Exception ignore) {
207                         // ignored
208                     }
209                 } catch (final Exception e) {
210                     e.printStackTrace();
211                     failed = true;
212                     complete = true;
213                     break;
214                 }
215             }
216             complete = true;
217         }
218     }
219 
220     private static final Duration MAX_WAIT_DURATION = Duration.ofMillis(100);
221 
222     private static final boolean DISPLAY_THREAD_DETAILS = Boolean.getBoolean("TestConnectionPool.display.thread.details");
223     // To pass this to a Maven test, use:
224     // mvn test -DargLine="-DTestConnectionPool.display.thread.details=true"
225     // @see https://issues.apache.org/jira/browse/SUREFIRE-121
226 
227     private static int currentThreadCount;
228 
229     private static final String DONE = "Done";
230 
231     /** Connections opened during the course of a test */
232     protected final Stack<Connection> connectionStack = new Stack<>();
233 
234     protected void assertBackPointers(final Connection conn, final Statement statement) throws SQLException {
235         assertFalse(conn.isClosed());
236         assertFalse(isClosed(statement));
237 
238         assertSame(conn, statement.getConnection(),
239                 "statement.getConnection() should return the exact same connection instance that was used to create the statement");
240 
241         final ResultSet resultSet = statement.getResultSet();
242         assertFalse(isClosed(resultSet));
243         assertSame(statement, resultSet.getStatement(),
244                 "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
245 
246         final ResultSet executeResultSet = statement.executeQuery("select * from dual");
247         assertFalse(isClosed(executeResultSet));
248         assertSame(statement, executeResultSet.getStatement(),
249                 "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
250 
251         final ResultSet keysResultSet = statement.getGeneratedKeys();
252         assertFalse(isClosed(keysResultSet));
253         assertSame(statement, keysResultSet.getStatement(),
254                 "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
255 
256         ResultSet preparedResultSet = null;
257         if (statement instanceof PreparedStatement) {
258             final PreparedStatement preparedStatement = (PreparedStatement) statement;
259             preparedResultSet = preparedStatement.executeQuery();
260             assertFalse(isClosed(preparedResultSet));
261             assertSame(statement, preparedResultSet.getStatement(),
262                     "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
263         }
264 
265         resultSet.getStatement().getConnection().close();
266         assertTrue(conn.isClosed());
267         assertTrue(isClosed(statement));
268         assertTrue(isClosed(resultSet));
269         assertTrue(isClosed(executeResultSet));
270         assertTrue(isClosed(keysResultSet));
271         if (preparedResultSet != null) {
272             assertTrue(isClosed(preparedResultSet));
273         }
274     }
275 
276     protected abstract Connection getConnection() throws Exception;
277 
278     protected int getMaxTotal() {
279         return 10;
280     }
281 
282     protected Duration getMaxWaitDuration() {
283         return MAX_WAIT_DURATION;
284     }
285 
286     protected String getUsername(final Connection conn) throws SQLException {
287         try (final Statement stmt = conn.createStatement(); final ResultSet rs = stmt.executeQuery("select username")) {
288             if (rs.next()) {
289                 return rs.getString(1);
290             }
291         }
292         return null;
293     }
294 
295     protected boolean isClosed(final ResultSet resultSet) {
296         try {
297             resultSet.getWarnings();
298             return false;
299         } catch (final SQLException e) {
300             // getWarnings throws an exception if the statement is
301             // closed, but could throw an exception for other reasons
302             // in this case it is good enough to assume the result set
303             // is closed
304             return true;
305         }
306     }
307 
308     protected boolean isClosed(final Statement statement) {
309         try {
310             statement.getWarnings();
311             return false;
312         } catch (final SQLException e) {
313             // getWarnings throws an exception if the statement is
314             // closed, but could throw an exception for other reasons
315             // in this case it is good enough to assume the statement
316             // is closed
317             return true;
318         }
319     }
320 
321     /**
322      * Launches a group of 2 * getMaxTotal() threads, each of which will attempt to obtain a connection from the pool, hold it for {@code holdTime} ms, and then
323      * return it to the pool. If {@code loopOnce} is false, threads will continue this process indefinitely. If {@code expectError} is true, exactly 1/2 of the
324      * threads are expected to either throw exceptions or fail to complete. If {@code expectError} is false, all threads are expected to complete successfully.
325      *
326      * @param holdDuration    Duration that a thread holds a connection before returning it to the pool
327      * @param expectError     whether or not an error is expected
328      * @param loopOnce        whether threads should complete the borrow - hold - return cycle only once, or loop indefinitely
329      * @param maxWaitDuration passed in by client - has no impact on the test itself, but does get reported
330      * @throws Exception
331      */
332     protected void multipleThreads(final Duration holdDuration, final boolean expectError, final boolean loopOnce, final Duration maxWaitDuration)
333             throws Exception {
334         multipleThreads(holdDuration, expectError, loopOnce, maxWaitDuration, 1, 2 * getMaxTotal(), 300);
335     }
336 
337     /**
338      * Launches a group of {@code numThreads} threads, each of which will attempt to obtain a connection from the pool, hold it for {@code holdTime} ms, and
339      * then return it to the pool. If {@code loopOnce} is false, threads will continue this process indefinitely. If {@code expectError} is true, exactly 1/2 of
340      * the threads are expected to either throw exceptions or fail to complete. If {@code expectError} is false, all threads are expected to complete
341      * successfully. Threads are stopped after {@code duration} ms.
342      *
343      * @param holdDuration    Duration that a thread holds a connection before returning it to the pool
344      * @param expectError     whether or not an error is expected
345      * @param loopOnce        whether threads should complete the borrow - hold - return cycle only once, or loop indefinitely
346      * @param maxWaitDuration passed in by client - has no impact on the test itself, but does get reported
347      * @param numThreads      the number of threads
348      * @param duration        duration in ms of test
349      * @throws Exception
350      */
351     protected void multipleThreads(final Duration holdDuration, final boolean expectError, final boolean loopOnce, final Duration maxWaitDuration,
352             final int numStatements, final int numThreads, final long duration) throws Exception {
353         final long startTimeMillis = timeStampMillis();
354         final PoolTest[] pts = new PoolTest[numThreads];
355         // Catch Exception so we can stop all threads if one fails
356         final ThreadGroup threadGroup = new ThreadGroup("foo") {
357             @Override
358             public void uncaughtException(final Thread t, final Throwable e) {
359                 for (final PoolTest pt : pts) {
360                     pt.stop();
361                 }
362             }
363         };
364         // Create all the threads
365         for (int i = 0; i < pts.length; i++) {
366             pts[i] = new PoolTest(threadGroup, holdDuration, expectError, loopOnce, numStatements);
367         }
368         // Start all the threads
369         for (final PoolTest pt : pts) {
370             pt.start();
371         }
372 
373         // Give all threads a chance to start and succeed
374         Thread.sleep(duration);
375 
376         // Stop threads
377         for (final PoolTest pt : pts) {
378             pt.stop();
379         }
380 
381         /*
382          * Wait for all threads to terminate. This is essential to ensure that all threads have a chance to update success[0] and to ensure that the variable is
383          * published correctly.
384          */
385         int done = 0;
386         int failed = 0;
387         int didNotRun = 0;
388         int loops = 0;
389         for (final PoolTest poolTest : pts) {
390             poolTest.thread.join();
391             loops += poolTest.loops;
392             final String state = poolTest.state;
393             if (DONE.equals(state)) {
394                 done++;
395             }
396             if (poolTest.loops == 0) {
397                 didNotRun++;
398             }
399             final Throwable thrown = poolTest.thrown;
400             if (thrown != null) {
401                 failed++;
402                 if (!expectError || !(thrown instanceof SQLException)) {
403                     System.err.println("Unexpected error: " + thrown.getMessage());
404                 }
405             }
406         }
407 
408         final long timeMillis = timeStampMillis() - startTimeMillis;
409         // @formatter:off
410         println("Multithread test time = " + timeMillis + " ms. Threads: " + pts.length + ". Loops: " + loops + ". Hold time: " + holdDuration
411                 + ". maxWaitMillis: " + maxWaitDuration + ". Done: " + done + ". Did not run: " + didNotRun + ". Failed: " + failed + ". expectError: "
412                 + expectError);
413         // @formatter:on
414         if (expectError) {
415             if (DISPLAY_THREAD_DETAILS || pts.length / 2 != failed) {
416                 final long offset = pts[0].createdMillis - 1000; // To reduce size of output numbers, but ensure they have 4 digits
417                 println("Offset: " + offset);
418                 for (int i = 0; i < pts.length; i++) {
419                     final PoolTest pt = pts[i];
420                     // @formatter:off
421                     println("Pre: " + (pt.preconnected - offset) // First, so can sort on this easily
422                             + ". Post: " + (pt.postconnected != 0 ? Long.toString(pt.postconnected - offset) : "-") + ". Hash: " + pt.connHash + ". Startup: "
423                             + (pt.started - pt.createdMillis) + ". getConn(): " + (pt.connected != 0 ? Long.toString(pt.connected - pt.preconnected) : "-")
424                             + ". Runtime: " + (pt.ended - pt.started) + ". IDX: " + i + ". Loops: " + pt.loops + ". State: " + pt.state + ". thrown: "
425                             + pt.thrown + ".");
426                     // @formatter:on
427                 }
428             }
429             if (didNotRun > 0) {
430                 println("NOTE: some threads did not run the code: " + didNotRun);
431             }
432             // Perform initial sanity check:
433             assertTrue(failed > 0, "Expected some of the threads to fail");
434             // Assume that threads that did not run would have timed out.
435             assertEquals(pts.length / 2, failed + didNotRun, "WARNING: Expected half the threads to fail");
436         } else {
437             assertEquals(0, failed, "Did not expect any threads to fail");
438         }
439     }
440 
441     /**
442      * Acquires a new connection and push it onto the connections stack.
443      *
444      * @return a new connection.
445      * @throws Exception Defined in subclasses.
446      */
447     @SuppressWarnings("resource") // Caller closes
448     protected Connection newConnection() throws Exception {
449         return connectionStack.push(getConnection());
450     }
451 
452     void println(final String string) {
453         if (Boolean.getBoolean(getClass().getSimpleName() + ".debug")) {
454             System.out.println(string);
455         }
456     }
457 
458     @AfterEach
459     public void tearDown() throws Exception {
460         // Close any connections opened by the test
461         while (!connectionStack.isEmpty()) {
462             Utils.closeQuietly((AutoCloseable) connectionStack.pop());
463         }
464     }
465 
466     @Test
467     public void testAutoCommitBehavior() throws Exception {
468         final Connection conn0 = newConnection();
469         assertNotNull(conn0, "connection should not be null");
470         assertTrue(conn0.getAutoCommit(), "autocommit should be true for conn0");
471 
472         final Connection conn1 = newConnection();
473         assertTrue(conn1.getAutoCommit(), "autocommit should be true for conn1");
474         conn1.close();
475 
476         assertTrue(conn0.getAutoCommit(), "autocommit should be true for conn0");
477         conn0.setAutoCommit(false);
478         assertFalse(conn0.getAutoCommit(), "autocommit should be false for conn0");
479         conn0.close();
480 
481         final Connection conn2 = newConnection();
482         assertTrue(conn2.getAutoCommit(), "autocommit should be true for conn2");
483 
484         final Connection conn3 = newConnection();
485         assertTrue(conn3.getAutoCommit(), "autocommit should be true for conn3");
486 
487         conn2.close();
488 
489         conn3.close();
490     }
491 
492     @Test
493     void testBackPointers() throws Exception {
494         // normal statement
495         Connection conn = newConnection();
496         assertBackPointers(conn, conn.createStatement());
497         conn = newConnection();
498         assertBackPointers(conn, conn.createStatement(0, 0));
499         conn = newConnection();
500         assertBackPointers(conn, conn.createStatement(0, 0, 0));
501 
502         // prepared statement
503         conn = newConnection();
504         assertBackPointers(conn, conn.prepareStatement("select * from dual"));
505         conn = newConnection();
506         assertBackPointers(conn, conn.prepareStatement("select * from dual", 0));
507         conn = newConnection();
508         assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0));
509         conn = newConnection();
510         assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0, 0));
511         conn = newConnection();
512         assertBackPointers(conn, conn.prepareStatement("select * from dual", new int[0]));
513         conn = newConnection();
514         assertBackPointers(conn, conn.prepareStatement("select * from dual", new String[0]));
515 
516         // callable statement
517         conn = newConnection();
518         assertBackPointers(conn, conn.prepareCall("select * from dual"));
519         conn = newConnection();
520         assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0));
521         conn = newConnection();
522         assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0, 0));
523     }
524 
525     @Test
526     void testCanCloseCallableStatementTwice() throws Exception {
527         try (Connection conn = newConnection()) {
528             assertNotNull(conn);
529             assertFalse(conn.isClosed());
530             for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
531                 final PreparedStatement stmt = conn.prepareCall("select * from dual");
532                 assertNotNull(stmt);
533                 assertFalse(isClosed(stmt));
534                 stmt.close();
535                 assertTrue(isClosed(stmt));
536                 stmt.close();
537                 assertTrue(isClosed(stmt));
538                 stmt.close();
539                 assertTrue(isClosed(stmt));
540             }
541         }
542     }
543 
544     /**
545      * Verify the close method can be called multiple times on a single connection without an exception being thrown.
546      */
547     @Test
548     void testCanCloseConnectionTwice() throws Exception {
549         for (int i = 0; i < getMaxTotal(); i++) { // loop to show we *can* close again once we've borrowed it from the pool again
550             final Connection conn = newConnection();
551             assertNotNull(conn);
552             assertFalse(conn.isClosed());
553             conn.close();
554             assertTrue(conn.isClosed());
555             conn.close();
556             assertTrue(conn.isClosed());
557         }
558     }
559 
560     @Test
561     void testCanClosePreparedStatementTwice() throws Exception {
562         try (Connection conn = newConnection()) {
563             assertNotNull(conn);
564             assertFalse(conn.isClosed());
565             for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
566                 final PreparedStatement stmt = conn.prepareStatement("select * from dual");
567                 assertNotNull(stmt);
568                 assertFalse(isClosed(stmt));
569                 stmt.close();
570                 assertTrue(isClosed(stmt));
571                 stmt.close();
572                 assertTrue(isClosed(stmt));
573                 stmt.close();
574                 assertTrue(isClosed(stmt));
575             }
576         }
577     }
578 
579     @Test
580     void testCanCloseResultSetTwice() throws Exception {
581         try (Connection conn = newConnection()) {
582             assertNotNull(conn);
583             assertFalse(conn.isClosed());
584             for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
585                 final PreparedStatement stmt = conn.prepareStatement("select * from dual");
586                 assertNotNull(stmt);
587                 final ResultSet rset = stmt.executeQuery();
588                 assertNotNull(rset);
589                 assertFalse(isClosed(rset));
590                 rset.close();
591                 assertTrue(isClosed(rset));
592                 rset.close();
593                 assertTrue(isClosed(rset));
594                 rset.close();
595                 assertTrue(isClosed(rset));
596             }
597         }
598     }
599 
600     @Test
601     void testCanCloseStatementTwice() throws Exception {
602         final Connection conn = newConnection();
603         assertNotNull(conn);
604         assertFalse(conn.isClosed());
605         for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
606             final Statement stmt = conn.createStatement();
607             assertNotNull(stmt);
608             assertFalse(isClosed(stmt));
609             stmt.close();
610             assertTrue(isClosed(stmt));
611             stmt.close();
612             assertTrue(isClosed(stmt));
613             stmt.close();
614             assertTrue(isClosed(stmt));
615         }
616         conn.close();
617     }
618 
619     @Test
620     public void testClearWarnings() throws Exception {
621         final Connection[] c = new Connection[getMaxTotal()];
622         for (int i = 0; i < c.length; i++) {
623             c[i] = newConnection();
624             assertNotNull(c[i]);
625 
626             // generate SQLWarning on connection
627             try (CallableStatement cs = c[i].prepareCall("warning")) {
628                 // empty
629             }
630         }
631 
632         for (final Connection element : c) {
633             assertNotNull(element.getWarnings());
634         }
635 
636         for (final Connection element : c) {
637             element.close();
638         }
639 
640         for (int i = 0; i < c.length; i++) {
641             c[i] = newConnection();
642         }
643 
644         for (final Connection element : c) {
645             // warnings should have been cleared by putting the connection back in the pool
646             assertNull(element.getWarnings());
647         }
648 
649         for (final Connection element : c) {
650             element.close();
651         }
652     }
653 
654     @Test
655     public void testClosing() throws Exception {
656         final Connection[] c = new Connection[getMaxTotal()];
657         // open the maximum connections
658         for (int i = 0; i < c.length; i++) {
659             c[i] = newConnection();
660         }
661 
662         // close one of the connections
663         c[0].close();
664         assertTrue(c[0].isClosed());
665 
666         // get a new connection
667         c[0] = newConnection();
668 
669         for (final Connection element : c) {
670             element.close();
671         }
672     }
673 
674     /** "https://issues.apache.org/bugzilla/show_bug.cgi?id=12400" */
675     @Test
676     public void testConnectionsAreDistinct() throws Exception {
677         final Connection[] conn = new Connection[getMaxTotal()];
678         for (int i = 0; i < conn.length; i++) {
679             conn[i] = newConnection();
680             for (int j = 0; j < i; j++) {
681                 assertNotSame(conn[j], conn[i]);
682                 assertNotEquals(conn[j], conn[i]);
683             }
684         }
685         for (final Connection element : conn) {
686             element.close();
687         }
688     }
689 
690     // Bugzilla Bug 26966: Connectionpool's connections always returns same
691     @Test
692     public void testHashCode() throws Exception {
693         final Connection conn1 = newConnection();
694         assertNotNull(conn1);
695         final Connection conn2 = newConnection();
696         assertNotNull(conn2);
697 
698         assertTrue(conn1.hashCode() != conn2.hashCode());
699     }
700 
701     /**
702      * DBCP-128: BasicDataSource.getConnection() Connections don't work as hashtable keys
703      */
704     @Test
705     void testHashing() throws Exception {
706         final Connection con = getConnection();
707         final Hashtable<Connection, String> hash = new Hashtable<>();
708         hash.put(con, "test");
709         assertEquals("test", hash.get(con));
710         assertTrue(hash.containsKey(con));
711         assertTrue(hash.contains("test"));
712         hash.clear();
713         con.close();
714     }
715 
716     @Test
717     void testIsClosed() throws Exception {
718         for (int i = 0; i < getMaxTotal(); i++) {
719             @SuppressWarnings("resource")
720             final Connection conn = newConnection();
721             try {
722                 assertNotNull(conn);
723                 assertFalse(conn.isClosed());
724                 try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
725                     assertNotNull(stmt);
726                     try (ResultSet rset = stmt.executeQuery()) {
727                         assertNotNull(rset);
728                         assertTrue(rset.next());
729                     }
730                 }
731             } finally {
732                 conn.close();
733             }
734             assertTrue(conn.isClosed());
735         }
736     }
737 
738     @Test
739     public void testMaxTotal() throws Exception {
740         final Connection[] c = new Connection[getMaxTotal()];
741         for (int i = 0; i < c.length; i++) {
742             c[i] = newConnection();
743             assertNotNull(c[i]);
744         }
745 
746         // should only be able to open 10 connections, so this test should
747         // throw an exception
748         assertThrows(SQLException.class, this::newConnection);
749 
750         for (final Connection element : c) {
751             element.close();
752         }
753     }
754 
755     // Bugzilla Bug 24966: NullPointer with Oracle 9 driver
756     // wrong order of passivate/close when a rset isn't closed
757     @Test
758     void testNoRsetClose() throws Exception {
759         try (Connection conn = newConnection()) {
760             assertNotNull(conn);
761             try (PreparedStatement stmt = conn.prepareStatement("test")) {
762                 assertNotNull(stmt);
763                 final ResultSet rset = stmt.getResultSet();
764                 assertNotNull(rset);
765                 // forget to close the resultset: rset.close();
766             }
767         }
768     }
769 
770     @Test
771     public void testOpening() throws Exception {
772         final Connection[] c = new Connection[getMaxTotal()];
773         // test that opening new connections is not closing previous
774         for (int i = 0; i < c.length; i++) {
775             c[i] = newConnection();
776             assertNotNull(c[i]);
777             for (int j = 0; j <= i; j++) {
778                 assertFalse(c[j].isClosed());
779             }
780         }
781 
782         for (final Connection element : c) {
783             element.close();
784         }
785     }
786 
787     @Test
788     void testPooling() throws Exception {
789         // Grab a maximal set of open connections from the pool
790         final Connection[] c = new Connection[getMaxTotal()];
791         final Connection[] u = new Connection[getMaxTotal()];
792         for (int i = 0; i < c.length; i++) {
793             c[i] = newConnection();
794             if (!(c[i] instanceof DelegatingConnection)) {
795                 for (int j = 0; j <= i; j++) {
796                     c[j].close();
797                 }
798                 return; // skip this test
799             }
800             u[i] = ((DelegatingConnection<?>) c[i]).getInnermostDelegate();
801         }
802         // Close connections one at a time and get new ones, making sure
803         // the new ones come from the pool
804         for (final Connection element : c) {
805             element.close();
806             try (Connection con = newConnection()) {
807                 final Connection underCon = ((DelegatingConnection<?>) con).getInnermostDelegate();
808                 assertNotNull(underCon, "Failed to get connection");
809                 boolean found = false;
810                 for (int j = 0; j < c.length; j++) {
811                     if (underCon == u[j]) {
812                         found = true;
813                         break;
814                     }
815                 }
816                 assertTrue(found, "New connection not from pool");
817             }
818         }
819     }
820 
821     // Bugzilla Bug 24328: PooledConnectionImpl ignores resultsetType
822     // and Concurrency if statement pooling is not enabled
823     // https://issues.apache.org/bugzilla/show_bug.cgi?id=24328
824     @Test
825     void testPrepareStatementOptions() throws Exception {
826         try (Connection conn = newConnection()) {
827             assertNotNull(conn);
828             try (PreparedStatement stmt = conn.prepareStatement("select * from dual", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
829                 assertNotNull(stmt);
830                 try (ResultSet rset = stmt.executeQuery()) {
831                     assertNotNull(rset);
832                     assertTrue(rset.next());
833 
834                     assertEquals(ResultSet.TYPE_SCROLL_SENSITIVE, rset.getType());
835                     assertEquals(ResultSet.CONCUR_UPDATABLE, rset.getConcurrency());
836 
837                 }
838             }
839         }
840     }
841 
842     @Test
843     void testRepeatedBorrowAndReturn() throws Exception {
844         for (int i = 0; i < 100; i++) {
845             try (Connection conn = newConnection()) {
846                 assertNotNull(conn);
847                 try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
848                     assertNotNull(stmt);
849                     try (ResultSet rset = stmt.executeQuery()) {
850                         assertNotNull(rset);
851                         assertTrue(rset.next());
852                     }
853                 }
854             }
855         }
856     }
857 
858     @Test
859     public void testSimple() throws Exception {
860         try (Connection conn = newConnection()) {
861             assertNotNull(conn);
862             try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
863                 assertNotNull(stmt);
864                 try (ResultSet rset = stmt.executeQuery()) {
865                     assertNotNull(rset);
866                     assertTrue(rset.next());
867                 }
868             }
869         }
870     }
871 
872     @Test
873     public void testSimple2() throws Exception {
874         @SuppressWarnings("resource")
875         final Connection conn = newConnection();
876         assertNotNull(conn);
877         try {
878             try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
879                 assertNotNull(stmt);
880                 try (ResultSet rset = stmt.executeQuery()) {
881                     assertNotNull(rset);
882                     assertTrue(rset.next());
883                 }
884             }
885             try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
886                 assertNotNull(stmt);
887                 try (ResultSet rset = stmt.executeQuery()) {
888                     assertNotNull(rset);
889                     assertTrue(rset.next());
890                 }
891             }
892         } finally {
893             conn.close();
894         }
895         assertThrows(SQLException.class, conn::createStatement, "Can't use closed connections");
896 
897         try (Connection conn2 = newConnection()) {
898             assertNotNull(conn2);
899             {
900                 try (PreparedStatement stmt = conn2.prepareStatement("select * from dual")) {
901                     assertNotNull(stmt);
902                     try (ResultSet rset = stmt.executeQuery()) {
903                         assertNotNull(rset);
904                         assertTrue(rset.next());
905                     }
906                 }
907             }
908             {
909                 try (PreparedStatement stmt = conn2.prepareStatement("select * from dual")) {
910                     assertNotNull(stmt);
911                     try (ResultSet rset = stmt.executeQuery()) {
912                         assertNotNull(rset);
913                         assertTrue(rset.next());
914                     }
915                 }
916             }
917         }
918     }
919 
920     @Test
921     void testThreaded() {
922         final TestThread[] threads = new TestThread[getMaxTotal()];
923         for (int i = 0; i < threads.length; i++) {
924             threads[i] = new TestThread(50, 50);
925             final Thread t = new Thread(threads[i]);
926             t.start();
927         }
928         for (int i = 0; i < threads.length; i++) {
929             while (!threads[i].complete()) {
930                 try {
931                     Thread.sleep(100L);
932                 } catch (final Exception e) {
933                     // ignored
934                 }
935             }
936             if (threads[i] != null && threads[i].failed()) {
937                 fail("Thread failed: " + i);
938             }
939         }
940     }
941 
942     long timeStampMillis() {
943         return System.currentTimeMillis(); // JVM 1.5+ System.nanoTime() / 1000000;
944     }
945 }