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