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    *      http://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 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, final int numStatements) {
93              this.loopOnce = once;
94              this.connHoldDuration = connHoldDuration;
95              stopOnException = isStopOnException;
96              isRun = true; // Must be done here so main thread is guaranteed to be able to set it false
97              thrown = null;
98              thread =
99                  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 java.util.Random _random = new java.util.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             _iter = iter;
178         }
179 
180         public TestThread(final int iter, final int delay) {
181             _iter = iter;
182             _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=
223     Boolean.getBoolean("TestConnectionPool.display.thread.details");
224     // To pass this to a Maven test, use:
225     // mvn test -DargLine="-DTestConnectionPool.display.thread.details=true"
226     // @see https://issues.apache.org/jira/browse/SUREFIRE-121
227 
228     private static int currentThreadCount;
229 
230     private static final String DONE = "Done";
231 
232     /** Connections opened during the course of a test */
233     protected final Stack<Connection> connectionStack = new Stack<>();
234 
235     // ----------- Utility Methods ---------------------------------
236 
237     protected void assertBackPointers(final Connection conn, final Statement statement) throws SQLException {
238         assertFalse(conn.isClosed());
239         assertFalse(isClosed(statement));
240 
241         assertSame(conn, statement.getConnection(),
242                 "statement.getConnection() should return the exact same connection instance that was used to create the statement");
243 
244         final ResultSet resultSet = statement.getResultSet();
245         assertFalse(isClosed(resultSet));
246         assertSame(statement, resultSet.getStatement(),
247                 "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
248 
249         final ResultSet executeResultSet = statement.executeQuery("select * from dual");
250         assertFalse(isClosed(executeResultSet));
251         assertSame(statement, executeResultSet.getStatement(),
252                 "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
253 
254         final ResultSet keysResultSet = statement.getGeneratedKeys();
255         assertFalse(isClosed(keysResultSet));
256         assertSame(statement, keysResultSet.getStatement(),
257                 "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
258 
259         ResultSet preparedResultSet = null;
260         if (statement instanceof PreparedStatement) {
261             final PreparedStatement preparedStatement = (PreparedStatement) statement;
262             preparedResultSet = preparedStatement.executeQuery();
263             assertFalse(isClosed(preparedResultSet));
264             assertSame(statement, preparedResultSet.getStatement(),
265                     "resultSet.getStatement() should return the exact same statement instance that was used to create the result set");
266         }
267 
268         resultSet.getStatement().getConnection().close();
269         assertTrue(conn.isClosed());
270         assertTrue(isClosed(statement));
271         assertTrue(isClosed(resultSet));
272         assertTrue(isClosed(executeResultSet));
273         assertTrue(isClosed(keysResultSet));
274         if (preparedResultSet != null) {
275             assertTrue(isClosed(preparedResultSet));
276         }
277     }
278 
279     protected abstract Connection getConnection() throws Exception;
280 
281     protected int getMaxTotal() {
282         return 10;
283     }
284 
285     protected Duration getMaxWaitDuration() {
286         return MAX_WAIT_DURATION;
287     }
288 
289     protected String getUsername(final Connection conn) throws SQLException {
290         try (final Statement stmt = conn.createStatement(); final ResultSet rs = stmt.executeQuery("select username")) {
291             if (rs.next()) {
292                 return rs.getString(1);
293             }
294         }
295         return null;
296     }
297 
298     protected boolean isClosed(final ResultSet resultSet) {
299         try {
300             resultSet.getWarnings();
301             return false;
302         } catch (final SQLException e) {
303             // getWarnings throws an exception if the statement is
304             // closed, but could throw an exception for other reasons
305             // in this case it is good enough to assume the result set
306             // is closed
307             return true;
308         }
309     }
310 
311     protected boolean isClosed(final Statement statement) {
312         try {
313             statement.getWarnings();
314             return false;
315         } catch (final SQLException e) {
316             // getWarnings throws an exception if the statement is
317             // closed, but could throw an exception for other reasons
318             // in this case it is good enough to assume the statement
319             // is closed
320             return true;
321         }
322     }
323 
324     /**
325      * Launches a group of 2 * getMaxTotal() threads, each of which will attempt to obtain a connection
326      * from the pool, hold it for {@code holdTime} ms, and then return it to the pool.  If {@code loopOnce} is false,
327      * threads will continue this process indefinitely.  If {@code expectError} is true, exactly 1/2 of the
328      * threads are expected to either throw exceptions or fail to complete. If {@code expectError} is false,
329      * all threads are expected to complete successfully.
330      *
331      * @param holdDuration Duration that a thread holds a connection before returning it to the pool
332      * @param expectError whether or not an error is expected
333      * @param loopOnce whether threads should complete the borrow - hold - return cycle only once, or loop indefinitely
334      * @param maxWaitDuration passed in by client - has no impact on the test itself, but does get reported
335      *
336      * @throws Exception
337      */
338     protected void multipleThreads(final Duration holdDuration,
339         final boolean expectError, final boolean loopOnce,
340         final Duration maxWaitDuration) throws Exception {
341         multipleThreads(holdDuration, expectError, loopOnce, maxWaitDuration, 1, 2 * getMaxTotal(), 300);
342     }
343 
344     /**
345      * Launches a group of {@code numThreads} threads, each of which will attempt to obtain a connection
346      * from the pool, hold it for {@code holdTime} ms, and then return it to the pool.  If {@code loopOnce} is false,
347      * threads will continue this process indefinitely.  If {@code expectError} is true, exactly 1/2 of the
348      * threads are expected to either throw exceptions or fail to complete. If {@code expectError} is false,
349      * all threads are expected to complete successfully.  Threads are stopped after {@code duration} ms.
350      *
351      * @param holdDuration Duration that a thread holds a connection before returning it to the pool
352      * @param expectError whether or not an error is expected
353      * @param loopOnce whether threads should complete the borrow - hold - return cycle only once, or loop indefinitely
354      * @param maxWaitDuration passed in by client - has no impact on the test itself, but does get reported
355      * @param numThreads the number of threads
356      * @param duration duration in ms of test
357      *
358      * @throws Exception
359      */
360     protected void multipleThreads(final Duration holdDuration,
361             final boolean expectError, final boolean loopOnce,
362         final Duration maxWaitDuration, final int numStatements, final int numThreads, final long duration) throws Exception {
363         final long startTimeMillis = timeStampMillis();
364         final PoolTest[] pts = new PoolTest[numThreads];
365         // Catch Exception so we can stop all threads if one fails
366         final ThreadGroup threadGroup = new ThreadGroup("foo") {
367             @Override
368             public void uncaughtException(final Thread t, final Throwable e) {
369                 for (final PoolTest pt : pts) {
370                     pt.stop();
371                 }
372             }
373         };
374         // Create all the threads
375         for (int i = 0; i < pts.length; i++) {
376             pts[i] = new PoolTest(threadGroup, holdDuration, expectError, loopOnce, numStatements);
377         }
378         // Start all the threads
379         for (final PoolTest pt : pts) {
380             pt.start();
381         }
382 
383         // Give all threads a chance to start and succeed
384         Thread.sleep(duration);
385 
386         // Stop threads
387         for (final PoolTest pt : pts) {
388             pt.stop();
389         }
390 
391         /*
392          * Wait for all threads to terminate. This is essential to ensure that all threads have a chance to update success[0]
393          * and to ensure that the variable is published correctly.
394          */
395         int done = 0;
396         int failed = 0;
397         int didNotRun = 0;
398         int loops = 0;
399         for (final PoolTest poolTest : pts) {
400             poolTest.thread.join();
401             loops += poolTest.loops;
402             final String state = poolTest.state;
403             if (DONE.equals(state)) {
404                 done++;
405             }
406             if (poolTest.loops == 0) {
407                 didNotRun++;
408             }
409             final Throwable thrown = poolTest.thrown;
410             if (thrown != null) {
411                 failed++;
412                 if (!expectError || !(thrown instanceof SQLException)) {
413                     System.err.println("Unexpected error: " + thrown.getMessage());
414                 }
415             }
416         }
417 
418         final long timeMillis = timeStampMillis() - startTimeMillis;
419         // @formatter:off
420         println("Multithread test time = " + timeMillis
421             + " ms. Threads: " + pts.length
422             + ". Loops: " + loops
423             + ". Hold time: " + holdDuration
424             + ". maxWaitMillis: " + maxWaitDuration
425             + ". Done: " + done
426             + ". Did not run: " + didNotRun
427             + ". Failed: " + failed
428             + ". expectError: " + expectError);
429         // @formatter:on
430         if (expectError) {
431             if (DISPLAY_THREAD_DETAILS || pts.length / 2 != failed) {
432                 final long offset = pts[0].createdMillis - 1000; // To reduce size of output numbers, but ensure they have 4 digits
433                 println("Offset: " + offset);
434                 for (int i = 0; i < pts.length; i++) {
435                     final PoolTest pt = pts[i];
436                     // @formatter:off
437                     println("Pre: " + (pt.preconnected-offset) // First, so can sort on this easily
438                         + ". Post: " + (pt.postconnected != 0 ? Long.toString(pt.postconnected-offset): "-")
439                         + ". Hash: " + pt.connHash
440                         + ". Startup: " + (pt.started-pt.createdMillis)
441                         + ". getConn(): " + (pt.connected != 0 ? Long.toString(pt.connected-pt.preconnected) : "-")
442                         + ". Runtime: " + (pt.ended-pt.started)
443                         + ". IDX: " + i
444                         + ". Loops: " + pt.loops
445                         + ". State: " + pt.state
446                         + ". thrown: "+ pt.thrown
447                         + ".");
448                     // @formatter:on
449                 }
450             }
451             if (didNotRun > 0) {
452                 println("NOTE: some threads did not run the code: " + didNotRun);
453             }
454             // Perform initial sanity check:
455             assertTrue(failed > 0, "Expected some of the threads to fail");
456             // Assume that threads that did not run would have timed out.
457             assertEquals(pts.length / 2, failed + didNotRun, "WARNING: Expected half the threads to fail");
458         } else {
459             assertEquals(0, failed, "Did not expect any threads to fail");
460         }
461     }
462 
463     /**
464      * Acquires a new connection and push it onto the connections stack.
465      *
466      * @return a new connection.
467      * @throws Exception Defined in subclasses.
468      */
469     @SuppressWarnings("resource") // Caller closes
470     protected Connection newConnection() throws Exception {
471         return connectionStack.push(getConnection());
472     }
473 
474     void println(final String string) {
475         if (Boolean.getBoolean(getClass().getSimpleName() + ".debug")) {
476             System.out.println(string);
477         }
478     }
479 
480     @AfterEach
481     public void tearDown() throws Exception {
482         // Close any connections opened by the test
483         while (!connectionStack.isEmpty()) {
484             Utils.closeQuietly((AutoCloseable) connectionStack.pop());
485         }
486     }
487 
488     @Test
489     public void testAutoCommitBehavior() throws Exception {
490         final Connection conn0 = newConnection();
491         assertNotNull(conn0, "connection should not be null");
492         assertTrue(conn0.getAutoCommit(), "autocommit should be true for conn0");
493 
494         final Connection conn1 = newConnection();
495         assertTrue(conn1.getAutoCommit(), "autocommit should be true for conn1");
496         conn1.close();
497 
498         assertTrue(conn0.getAutoCommit(), "autocommit should be true for conn0");
499         conn0.setAutoCommit(false);
500         assertFalse(conn0.getAutoCommit(), "autocommit should be false for conn0");
501         conn0.close();
502 
503         final Connection conn2 = newConnection();
504         assertTrue(conn2.getAutoCommit(), "autocommit should be true for conn2");
505 
506         final Connection conn3 = newConnection();
507         assertTrue(conn3.getAutoCommit(), "autocommit should be true for conn3");
508 
509         conn2.close();
510 
511         conn3.close();
512     }
513 
514     @Test
515     public void testBackPointers() throws Exception {
516         // normal statement
517         Connection conn = newConnection();
518         assertBackPointers(conn, conn.createStatement());
519         conn = newConnection();
520         assertBackPointers(conn, conn.createStatement(0, 0));
521         conn = newConnection();
522         assertBackPointers(conn, conn.createStatement(0, 0, 0));
523 
524         // prepared statement
525         conn = newConnection();
526         assertBackPointers(conn, conn.prepareStatement("select * from dual"));
527         conn = newConnection();
528         assertBackPointers(conn, conn.prepareStatement("select * from dual", 0));
529         conn = newConnection();
530         assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0));
531         conn = newConnection();
532         assertBackPointers(conn, conn.prepareStatement("select * from dual", 0, 0, 0));
533         conn = newConnection();
534         assertBackPointers(conn, conn.prepareStatement("select * from dual", new int[0]));
535         conn = newConnection();
536         assertBackPointers(conn, conn.prepareStatement("select * from dual", new String[0]));
537 
538         // callable statement
539         conn = newConnection();
540         assertBackPointers(conn, conn.prepareCall("select * from dual"));
541         conn = newConnection();
542         assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0));
543         conn = newConnection();
544         assertBackPointers(conn, conn.prepareCall("select * from dual", 0, 0, 0));
545     }
546 
547     @Test
548     public void testCanCloseCallableStatementTwice() throws Exception {
549         try (Connection conn = newConnection()) {
550             assertNotNull(conn);
551             assertFalse(conn.isClosed());
552             for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
553                 final PreparedStatement stmt = conn.prepareCall("select * from dual");
554                 assertNotNull(stmt);
555                 assertFalse(isClosed(stmt));
556                 stmt.close();
557                 assertTrue(isClosed(stmt));
558                 stmt.close();
559                 assertTrue(isClosed(stmt));
560                 stmt.close();
561                 assertTrue(isClosed(stmt));
562             }
563         }
564     }
565 
566     /**
567      * Verify the close method can be called multiple times on a single connection without
568      * an exception being thrown.
569      */
570     @Test
571     public void testCanCloseConnectionTwice() throws Exception {
572         for (int i = 0; i < getMaxTotal(); i++) { // loop to show we *can* close again once we've borrowed it from the pool again
573             final Connection conn = newConnection();
574             assertNotNull(conn);
575             assertFalse(conn.isClosed());
576             conn.close();
577             assertTrue(conn.isClosed());
578             conn.close();
579             assertTrue(conn.isClosed());
580         }
581     }
582 
583     @Test
584     public void testCanClosePreparedStatementTwice() throws Exception {
585         try (Connection conn = newConnection()) {
586             assertNotNull(conn);
587             assertFalse(conn.isClosed());
588             for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
589                 final PreparedStatement stmt = conn.prepareStatement("select * from dual");
590                 assertNotNull(stmt);
591                 assertFalse(isClosed(stmt));
592                 stmt.close();
593                 assertTrue(isClosed(stmt));
594                 stmt.close();
595                 assertTrue(isClosed(stmt));
596                 stmt.close();
597                 assertTrue(isClosed(stmt));
598             }
599         }
600     }
601 
602     @Test
603     public void testCanCloseResultSetTwice() throws Exception {
604         try (Connection conn = newConnection()) {
605             assertNotNull(conn);
606             assertFalse(conn.isClosed());
607             for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
608                 final PreparedStatement stmt = conn.prepareStatement("select * from dual");
609                 assertNotNull(stmt);
610                 final ResultSet rset = stmt.executeQuery();
611                 assertNotNull(rset);
612                 assertFalse(isClosed(rset));
613                 rset.close();
614                 assertTrue(isClosed(rset));
615                 rset.close();
616                 assertTrue(isClosed(rset));
617                 rset.close();
618                 assertTrue(isClosed(rset));
619             }
620         }
621     }
622 
623     @Test
624     public void testCanCloseStatementTwice() throws Exception {
625         final Connection conn = newConnection();
626         assertNotNull(conn);
627         assertFalse(conn.isClosed());
628         for (int i = 0; i < 2; i++) { // loop to show we *can* close again once we've borrowed it from the pool again
629             final Statement stmt = conn.createStatement();
630             assertNotNull(stmt);
631             assertFalse(isClosed(stmt));
632             stmt.close();
633             assertTrue(isClosed(stmt));
634             stmt.close();
635             assertTrue(isClosed(stmt));
636             stmt.close();
637             assertTrue(isClosed(stmt));
638         }
639         conn.close();
640     }
641 
642     @Test
643     public void testClearWarnings() throws Exception {
644         final Connection[] c = new Connection[getMaxTotal()];
645         for (int i = 0; i < c.length; i++) {
646             c[i] = newConnection();
647             assertNotNull(c[i]);
648 
649             // generate SQLWarning on connection
650             try (CallableStatement cs = c[i].prepareCall("warning")) {
651                 // empty
652             }
653         }
654 
655         for (final Connection element : c) {
656             assertNotNull(element.getWarnings());
657         }
658 
659         for (final Connection element : c) {
660             element.close();
661         }
662 
663         for (int i = 0; i < c.length; i++) {
664             c[i] = newConnection();
665         }
666 
667         for (final Connection element : c) {
668             // warnings should have been cleared by putting the connection back in the pool
669             assertNull(element.getWarnings());
670         }
671 
672         for (final Connection element : c) {
673             element.close();
674         }
675     }
676 
677     @Test
678     public void testClosing() throws Exception {
679         final Connection[] c = new Connection[getMaxTotal()];
680         // open the maximum connections
681         for (int i = 0; i < c.length; i++) {
682             c[i] = newConnection();
683         }
684 
685         // close one of the connections
686         c[0].close();
687         assertTrue(c[0].isClosed());
688 
689         // get a new connection
690         c[0] = newConnection();
691 
692         for (final Connection element : c) {
693             element.close();
694         }
695     }
696 
697     /** "https://issues.apache.org/bugzilla/show_bug.cgi?id=12400" */
698     @Test
699     public void testConnectionsAreDistinct() throws Exception {
700         final Connection[] conn = new Connection[getMaxTotal()];
701         for(int i=0;i<conn.length;i++) {
702             conn[i] = newConnection();
703             for(int j=0;j<i;j++) {
704                 assertNotSame(conn[j], conn[i]);
705                 assertNotEquals(conn[j], conn[i]);
706             }
707         }
708         for (final Connection element : conn) {
709             element.close();
710         }
711     }
712 
713     // Bugzilla Bug 26966: Connectionpool's connections always returns same
714     @Test
715     public void testHashCode() throws Exception {
716         final Connection conn1 = newConnection();
717         assertNotNull(conn1);
718         final Connection conn2 = newConnection();
719         assertNotNull(conn2);
720 
721         assertTrue(conn1.hashCode() != conn2.hashCode());
722     }
723 
724     /**
725      * DBCP-128: BasicDataSource.getConnection()
726      * Connections don't work as hashtable keys
727      */
728     @Test
729     public void testHashing() throws Exception {
730         final Connection con = getConnection();
731         final Hashtable<Connection, String> hash = new Hashtable<>();
732         hash.put(con, "test");
733         assertEquals("test", hash.get(con));
734         assertTrue(hash.containsKey(con));
735         assertTrue(hash.contains("test"));
736         hash.clear();
737         con.close();
738     }
739 
740     @Test
741     public void testIsClosed() throws Exception {
742         for (int i = 0; i < getMaxTotal(); i++) {
743             @SuppressWarnings("resource")
744             final Connection conn = newConnection();
745             try {
746                 assertNotNull(conn);
747                 assertFalse(conn.isClosed());
748                 try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
749                     assertNotNull(stmt);
750                     try (ResultSet rset = stmt.executeQuery()) {
751                         assertNotNull(rset);
752                         assertTrue(rset.next());
753                     }
754                 }
755             } finally {
756                 conn.close();
757             }
758             assertTrue(conn.isClosed());
759         }
760     }
761 
762     @Test
763     public void testMaxTotal() throws Exception {
764         final Connection[] c = new Connection[getMaxTotal()];
765         for (int i = 0; i < c.length; i++) {
766             c[i] = newConnection();
767             assertNotNull(c[i]);
768         }
769 
770         // should only be able to open 10 connections, so this test should
771         // throw an exception
772         assertThrows(SQLException.class, this::newConnection);
773 
774         for (final Connection element : c) {
775             element.close();
776         }
777     }
778 
779     // Bugzilla Bug 24966: NullPointer with Oracle 9 driver
780     // wrong order of passivate/close when a rset isn't closed
781     @Test
782     public void testNoRsetClose() throws Exception {
783         try (Connection conn = newConnection()) {
784             assertNotNull(conn);
785             try (PreparedStatement stmt = conn.prepareStatement("test")) {
786                 assertNotNull(stmt);
787                 final ResultSet rset = stmt.getResultSet();
788                 assertNotNull(rset);
789                 // forget to close the resultset: rset.close();
790             }
791         }
792     }
793 
794     @Test
795     public void testOpening() throws Exception {
796         final Connection[] c = new Connection[getMaxTotal()];
797         // test that opening new connections is not closing previous
798         for (int i = 0; i < c.length; i++) {
799             c[i] = newConnection();
800             assertNotNull(c[i]);
801             for (int j = 0; j <= i; j++) {
802                 assertFalse(c[j].isClosed());
803             }
804         }
805 
806         for (final Connection element : c) {
807             element.close();
808         }
809     }
810 
811     @Test
812     public void testPooling() throws Exception {
813         // Grab a maximal set of open connections from the pool
814         final Connection[] c = new Connection[getMaxTotal()];
815         final Connection[] u = new Connection[getMaxTotal()];
816         for (int i = 0; i < c.length; i++) {
817             c[i] = newConnection();
818             if (!(c[i] instanceof DelegatingConnection)) {
819                 for (int j = 0; j <= i; j++) {
820                     c[j].close();
821                 }
822                 return; // skip this test
823             }
824             u[i] = ((DelegatingConnection<?>) c[i]).getInnermostDelegate();
825         }
826         // Close connections one at a time and get new ones, making sure
827         // the new ones come from the pool
828         for (final Connection element : c) {
829             element.close();
830             try (Connection con = newConnection()) {
831                 final Connection underCon = ((DelegatingConnection<?>) con).getInnermostDelegate();
832                 assertNotNull(underCon, "Failed to get connection");
833                 boolean found = false;
834                 for (int j = 0; j < c.length; j++) {
835                     if (underCon == u[j]) {
836                         found = true;
837                         break;
838                     }
839                 }
840                 assertTrue(found, "New connection not from pool");
841             }
842         }
843     }
844 
845     // Bugzilla Bug 24328: PooledConnectionImpl ignores resultsetType
846     // and Concurrency if statement pooling is not enabled
847     // https://issues.apache.org/bugzilla/show_bug.cgi?id=24328
848     @Test
849     public void testPrepareStatementOptions() throws Exception {
850         try (Connection conn = newConnection()) {
851             assertNotNull(conn);
852             try (PreparedStatement stmt = conn.prepareStatement("select * from dual", ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) {
853                 assertNotNull(stmt);
854                 try (ResultSet rset = stmt.executeQuery()) {
855                     assertNotNull(rset);
856                     assertTrue(rset.next());
857 
858                     assertEquals(ResultSet.TYPE_SCROLL_SENSITIVE, rset.getType());
859                     assertEquals(ResultSet.CONCUR_UPDATABLE, rset.getConcurrency());
860 
861                 }
862             }
863         }
864     }
865 
866     @Test
867     public void testRepeatedBorrowAndReturn() throws Exception {
868         for (int i = 0; i < 100; i++) {
869             try (Connection conn = newConnection()) {
870                 assertNotNull(conn);
871                 try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
872                     assertNotNull(stmt);
873                     try (ResultSet rset = stmt.executeQuery()) {
874                         assertNotNull(rset);
875                         assertTrue(rset.next());
876                     }
877                 }
878             }
879         }
880     }
881 
882     @Test
883     public void testSimple() throws Exception {
884         try (Connection conn = newConnection()) {
885             assertNotNull(conn);
886             try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
887                 assertNotNull(stmt);
888                 try (ResultSet rset = stmt.executeQuery()) {
889                     assertNotNull(rset);
890                     assertTrue(rset.next());
891                 }
892             }
893         }
894     }
895 
896     @Test
897     public void testSimple2() throws Exception {
898         @SuppressWarnings("resource")
899         final Connection conn = newConnection();
900         assertNotNull(conn);
901         try {
902             try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
903                 assertNotNull(stmt);
904                 try (ResultSet rset = stmt.executeQuery()) {
905                     assertNotNull(rset);
906                     assertTrue(rset.next());
907                 }
908             }
909             try (PreparedStatement stmt = conn.prepareStatement("select * from dual")) {
910                 assertNotNull(stmt);
911                 try (ResultSet rset = stmt.executeQuery()) {
912                     assertNotNull(rset);
913                     assertTrue(rset.next());
914                 }
915             }
916         } finally {
917             conn.close();
918         }
919         assertThrows(SQLException.class, conn::createStatement, "Can't use closed connections");
920 
921         try (Connection conn2 = newConnection()) {
922             assertNotNull(conn2);
923             {
924                 try (PreparedStatement stmt = conn2.prepareStatement("select * from dual")) {
925                     assertNotNull(stmt);
926                     try (ResultSet rset = stmt.executeQuery()) {
927                         assertNotNull(rset);
928                         assertTrue(rset.next());
929                     }
930                 }
931             }
932             {
933                 try (PreparedStatement stmt = conn2.prepareStatement("select * from dual")) {
934                     assertNotNull(stmt);
935                     try (ResultSet rset = stmt.executeQuery()) {
936                         assertNotNull(rset);
937                         assertTrue(rset.next());
938                     }
939                 }
940             }
941         }
942     }
943 
944     @Test
945     public void testThreaded() {
946         final TestThread[] threads = new TestThread[getMaxTotal()];
947         for (int i = 0; i < threads.length; i++) {
948             threads[i] = new TestThread(50, 50);
949             final Thread t = new Thread(threads[i]);
950             t.start();
951         }
952         for (int i = 0; i < threads.length; i++) {
953             while (!threads[i].complete()) {
954                 try {
955                     Thread.sleep(100L);
956                 } catch (final Exception e) {
957                     // ignored
958                 }
959             }
960             if (threads[i] != null && threads[i].failed()) {
961                 fail("Thread failed: " + i);
962             }
963         }
964     }
965 
966     long timeStampMillis() {
967         return System.currentTimeMillis();// JVM 1.5+ System.nanoTime() / 1000000;
968     }
969 }