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