[SDNC-5] Rebase sdnc-core
[sdnc/core.git] / dblib / common / src / main / java / org / apache / tomcat / jdbc / pool / ConnectionPool.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * openecomp
4  * ================================================================================
5  * Copyright (C) 2016 - 2017 AT&T
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 /*
22  * Licensed to the Apache Software Foundation (ASF) under one or more
23  * contributor license agreements.  See the NOTICE file distributed with
24  * this work for additional information regarding copyright ownership.
25  * The ASF licenses this file to You under the Apache License, Version 2.0
26  * (the "License"); you may not use this file except in compliance with
27  * the License.  You may obtain a copy of the License at
28  *
29  *      http://www.apache.org/licenses/LICENSE-2.0
30  *
31  * Unless required by applicable law or agreed to in writing, software
32  * distributed under the License is distributed on an "AS IS" BASIS,
33  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34  * See the License for the specific language governing permissions and
35  * limitations under the License.
36  */
37 package org.apache.tomcat.jdbc.pool;
38
39 import java.lang.ref.WeakReference;
40 import java.lang.reflect.Constructor;
41 import java.lang.reflect.InvocationHandler;
42 import java.lang.reflect.Proxy;
43 import java.security.AccessController;
44 import java.security.PrivilegedAction;
45 import java.sql.Connection;
46 import java.sql.SQLException;
47 import java.util.Collections;
48 import java.util.ConcurrentModificationException;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.Set;
52 import java.util.Timer;
53 import java.util.TimerTask;
54 import java.util.concurrent.BlockingQueue;
55 import java.util.concurrent.CountDownLatch;
56 import java.util.concurrent.ExecutionException;
57 import java.util.concurrent.Future;
58 import java.util.concurrent.LinkedBlockingQueue;
59 import java.util.concurrent.ThreadPoolExecutor;
60 import java.util.concurrent.TimeUnit;
61 import java.util.concurrent.TimeoutException;
62 import java.util.concurrent.atomic.AtomicBoolean;
63 import java.util.concurrent.atomic.AtomicInteger;
64 import java.util.concurrent.atomic.AtomicLong;
65
66 import org.apache.juli.logging.Log;
67 import org.apache.juli.logging.LogFactory;
68
69 /**
70  * Implementation of simple connection pool.
71  * The ConnectionPool uses a {@link PoolProperties} object for storing all the meta information about the connection pool.
72  * As the underlying implementation, the connection pool uses {@link java.util.concurrent.BlockingQueue} to store active and idle connections.
73  * A custom implementation of a fair {@link FairBlockingQueue} blocking queue is provided with the connection pool itself.
74  * @version 1.0
75  */
76 public class ConnectionPool {
77
78     /**
79      * Default domain for objects registering with an mbean server
80      */
81     public static final String POOL_JMX_DOMAIN = "tomcat.jdbc";
82     /**
83      * Prefix type for JMX registration
84      */
85     public static final String POOL_JMX_TYPE_PREFIX = POOL_JMX_DOMAIN+":type=";
86
87     /**
88      * Logger
89      */
90     private static final Log log = LogFactory.getLog(ConnectionPool.class);
91
92     //===============================================================================
93     //         INSTANCE/QUICK ACCESS VARIABLE
94     //===============================================================================
95     /**
96      * Carries the size of the pool, instead of relying on a queue implementation
97      * that usually iterates over to get an exact count
98      */
99     private AtomicInteger size = new AtomicInteger(0);
100
101     /**
102      * All the information about the connection pool
103      * These are the properties the pool got instantiated with
104      */
105     private PoolConfiguration poolProperties;
106
107     /**
108      * Contains all the connections that are in use
109      * TODO - this shouldn't be a blocking queue, simply a list to hold our objects
110      */
111     private BlockingQueue<PooledConnection> busy;
112
113     /**
114      * Contains all the idle connections
115      */
116     private BlockingQueue<PooledConnection> idle;
117
118     /**
119      * The thread that is responsible for checking abandoned and idle threads
120      */
121     private volatile PoolCleaner poolCleaner;
122
123     /**
124      * Pool closed flag
125      */
126     private volatile boolean closed = false;
127
128     /**
129      * Since newProxyInstance performs the same operation, over and over
130      * again, it is much more optimized if we simply store the constructor ourselves.
131      */
132     private Constructor<?> proxyClassConstructor;
133
134     /**
135      * Executor service used to cancel Futures
136      */
137     private ThreadPoolExecutor cancellator = new ThreadPoolExecutor(0,1,1000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
138
139     /**
140      * reference to the JMX mbean
141      */
142     protected org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmxPool = null;
143
144     /**
145      * counter to track how many threads are waiting for a connection
146      */
147     private AtomicInteger waitcount = new AtomicInteger(0);
148
149     private AtomicLong poolVersion = new AtomicLong(Long.MIN_VALUE);
150
151     /**
152      * The counters for statistics of the pool.
153      */
154     private final AtomicLong borrowedCount = new AtomicLong(0);
155     private final AtomicLong returnedCount = new AtomicLong(0);
156     private final AtomicLong createdCount = new AtomicLong(0);
157     private final AtomicLong releasedCount = new AtomicLong(0);
158     private final AtomicLong reconnectedCount = new AtomicLong(0);
159     private final AtomicLong removeAbandonedCount = new AtomicLong(0);
160     private final AtomicLong releasedIdleCount = new AtomicLong(0);
161
162     //===============================================================================
163     //         PUBLIC METHODS
164     //===============================================================================
165
166     /**
167      * Instantiate a connection pool. This will create connections if initialSize is larger than 0.
168      * The {@link PoolProperties} should not be reused for another connection pool.
169      * @param prop PoolProperties - all the properties for this connection pool
170      * @throws SQLException Pool initialization error
171      */
172     public ConnectionPool(PoolConfiguration prop) throws SQLException {
173         //setup quick access variables and pools
174         init(prop);
175     }
176
177
178     /**
179      * Retrieves a Connection future. If a connection is not available, one can block using future.get()
180      * until a connection has become available.
181      * If a connection is not retrieved, the Future must be cancelled in order for the connection to be returned
182      * to the pool.
183      * @return a Future containing a reference to the connection or the future connection
184      * @throws SQLException Cannot use asynchronous connect
185      */
186     public Future<Connection> getConnectionAsync() throws SQLException {
187         try {
188             PooledConnection pc = borrowConnection(0, null, null);
189             if (pc!=null) {
190                 return new ConnectionFuture(pc);
191             }
192         }catch (SQLException x) {
193             if (x.getMessage().indexOf("NoWait")<0) {
194                 throw x;
195             }
196         }
197         //we can only retrieve a future if the underlying queue supports it.
198         if (idle instanceof FairBlockingQueue<?>) {
199             Future<PooledConnection> pcf = ((FairBlockingQueue<PooledConnection>)idle).pollAsync();
200             return new ConnectionFuture(pcf);
201         } else if (idle instanceof MultiLockFairBlockingQueue<?>) {
202                 Future<PooledConnection> pcf = ((MultiLockFairBlockingQueue<PooledConnection>)idle).pollAsync();
203                 return new ConnectionFuture(pcf);
204         } else {
205             throw new SQLException("Connection pool is misconfigured, doesn't support async retrieval. Set the 'fair' property to 'true'");
206         }
207     }
208
209     /**
210      * Borrows a connection from the pool. If a connection is available (in the idle queue) or the pool has not reached
211      * {@link PoolProperties#maxActive maxActive} connections a connection is returned immediately.
212      * If no connection is available, the pool will attempt to fetch a connection for {@link PoolProperties#maxWait maxWait} milliseconds.
213      * @return Connection - a java.sql.Connection/javax.sql.PooledConnection reflection proxy, wrapping the underlying object.
214      * @throws SQLException - if the wait times out or a failure occurs creating a connection
215      */
216     public Connection getConnection() throws SQLException {
217         //check out a connection
218         PooledConnection con = borrowConnection(-1,null,null);
219         return setupConnection(con);
220     }
221
222
223     /**
224      * Borrows a connection from the pool. If a connection is available (in the
225      * idle queue) or the pool has not reached {@link PoolProperties#maxActive
226      * maxActive} connections a connection is returned immediately. If no
227      * connection is available, the pool will attempt to fetch a connection for
228      * {@link PoolProperties#maxWait maxWait} milliseconds.
229      * @param username The user name to use for the connection
230      * @param password The password for the connection
231      * @return Connection - a java.sql.Connection/javax.sql.PooledConnection
232      *         reflection proxy, wrapping the underlying object.
233      * @throws SQLException
234      *             - if the wait times out or a failure occurs creating a
235      *             connection
236      */
237     public Connection getConnection(String username, String password) throws SQLException {
238         // check out a connection
239         PooledConnection con = borrowConnection(-1, username, password);
240         return setupConnection(con);
241     }
242
243     /**
244      * Returns the name of this pool
245      * @return String - the name of the pool
246      */
247     public String getName() {
248         return getPoolProperties().getPoolName();
249     }
250
251     /**
252      * Return the number of threads waiting for a connection
253      * @return number of threads waiting for a connection
254      */
255     public int getWaitCount() {
256         return waitcount.get();
257     }
258
259     /**
260      * Returns the pool properties associated with this connection pool
261      * @return PoolProperties
262      *
263      */
264     public PoolConfiguration getPoolProperties() {
265         return this.poolProperties;
266     }
267
268     /**
269      * Returns the total size of this pool, this includes both busy and idle connections
270      * @return int - number of established connections to the database
271      */
272     public int getSize() {
273         return size.get();
274     }
275
276     /**
277      * Returns the number of connections that are in use
278      * @return int - number of established connections that are being used by the application
279      */
280     public int getActive() {
281         return busy.size();
282     }
283
284     /**
285      * Returns the number of idle connections
286      * @return int - number of established connections not being used
287      */
288     public int getIdle() {
289         return idle.size();
290     }
291
292     /**
293      * Returns true if {@link #close close} has been called, and the connection pool is unusable
294      * @return boolean
295      */
296     public  boolean isClosed() {
297         return this.closed;
298     }
299
300     //===============================================================================
301     //         PROTECTED METHODS
302     //===============================================================================
303
304
305     /**
306      * configures a pooled connection as a proxy.
307      * This Proxy implements {@link java.sql.Connection} and {@link javax.sql.PooledConnection} interfaces.
308      * All calls on {@link java.sql.Connection} methods will be propagated down to the actual JDBC connection except for the
309      * {@link java.sql.Connection#close()} method.
310      * @param con a {@link PooledConnection} to wrap in a Proxy
311      * @return a {@link java.sql.Connection} object wrapping a pooled connection.
312      * @throws SQLException if an interceptor can't be configured, if the proxy can't be instantiated
313      */
314     protected Connection setupConnection(PooledConnection con) throws SQLException {
315         //fetch previously cached interceptor proxy - one per connection
316         JdbcInterceptor handler = con.getHandler();
317         if (handler==null) {
318             //build the proxy handler
319             handler = new ProxyConnection(this,con,getPoolProperties().isUseEquals());
320             //set up the interceptor chain
321             PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray();
322             for (int i=proxies.length-1; i>=0; i--) {
323                 try {
324                     //create a new instance
325                     JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance();
326                     //configure properties
327                     interceptor.setProperties(proxies[i].getProperties());
328                     //setup the chain
329                     interceptor.setNext(handler);
330                     //call reset
331                     interceptor.reset(this, con);
332                     //configure the last one to be held by the connection
333                     handler = interceptor;
334                 }catch(Exception x) {
335                     SQLException sx = new SQLException("Unable to instantiate interceptor chain.");
336                     sx.initCause(x);
337                     throw sx;
338                 }
339             }
340             //cache handler for the next iteration
341             con.setHandler(handler);
342         } else {
343             JdbcInterceptor next = handler;
344             //we have a cached handler, reset it
345             while (next!=null) {
346                 next.reset(this, con);
347                 next = next.getNext();
348             }
349         }
350
351         try {
352             getProxyConstructor(con.getXAConnection() != null);
353             //create the proxy
354             //TODO possible optimization, keep track if this connection was returned properly, and don't generate a new facade
355             Connection connection = null;
356             if (getPoolProperties().getUseDisposableConnectionFacade() ) {
357                 connection = (Connection)proxyClassConstructor.newInstance(new Object[] { new DisposableConnectionFacade(handler) });
358             } else {
359                 connection = (Connection)proxyClassConstructor.newInstance(new Object[] {handler});
360             }
361             //return the connection
362             return connection;
363         }catch (Exception x) {
364             SQLException s = new SQLException();
365             s.initCause(x);
366             throw s;
367         }
368
369     }
370
371     /**
372      * Creates and caches a {@link java.lang.reflect.Constructor} used to instantiate the proxy object.
373      * We cache this, since the creation of a constructor is fairly slow.
374      * @param xa Use a XA connection
375      * @return constructor used to instantiate the wrapper object
376      * @throws NoSuchMethodException Failed to get a constructor
377      */
378     public Constructor<?> getProxyConstructor(boolean xa) throws NoSuchMethodException {
379         //cache the constructor
380         if (proxyClassConstructor == null ) {
381             Class<?> proxyClass = xa ?
382                 Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.PooledConnection.class, javax.sql.XAConnection.class}) :
383                 Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.PooledConnection.class});
384             proxyClassConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
385         }
386         return proxyClassConstructor;
387     }
388
389     /**
390      * Closes the pool and all disconnects all idle connections
391      * Active connections will be closed upon the {@link java.sql.Connection#close close} method is called
392      * on the underlying connection instead of being returned to the pool
393      * @param force - true to even close the active connections
394      */
395     protected void close(boolean force) {
396         //are we already closed
397         if (this.closed) return;
398         //prevent other threads from entering
399         this.closed = true;
400         //stop background thread
401         if (poolCleaner!=null) {
402             poolCleaner.stopRunning();
403         }
404
405         /* release all idle connections */
406         BlockingQueue<PooledConnection> pool = (idle.size()>0)?idle:(force?busy:idle);
407         while (pool.size()>0) {
408             try {
409                 //retrieve the next connection
410                 PooledConnection con = pool.poll(1000, TimeUnit.MILLISECONDS);
411                 //close it and retrieve the next one, if one is available
412                 while (con != null) {
413                     //close the connection
414                     if (pool==idle)
415                         release(con);
416                     else
417                         abandon(con);
418                     if (pool.size()>0) {
419                         con = pool.poll(1000, TimeUnit.MILLISECONDS);
420                     } else {
421                         break;
422                     }
423                 } //while
424             } catch (InterruptedException ex) {
425                 if (getPoolProperties().getPropagateInterruptState()) {
426                     Thread.currentThread().interrupt();
427                 }
428             }
429             if (pool.size()==0 && force && pool!=busy) pool = busy;
430         }
431         if (this.getPoolProperties().isJmxEnabled()) this.jmxPool = null;
432         PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray();
433         for (int i=0; i<proxies.length; i++) {
434             try {
435                 JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance();
436                 interceptor.setProperties(proxies[i].getProperties());
437                 interceptor.poolClosed(this);
438             }catch (Exception x) {
439                 log.debug("Unable to inform interceptor of pool closure.",x);
440             }
441         }
442     } //closePool
443
444
445     /**
446      * Initialize the connection pool - called from the constructor
447      * @param properties PoolProperties - properties used to initialize the pool with
448      * @throws SQLException if initialization fails
449      */
450     protected void init(PoolConfiguration properties) throws SQLException {
451         poolProperties = properties;
452
453         //make sure the pool is properly configured
454         checkPoolConfiguration(properties);
455
456         //make space for 10 extra in case we flow over a bit
457         busy = new LinkedBlockingQueue<>();
458         //busy = new FairBlockingQueue<PooledConnection>();
459         //make space for 10 extra in case we flow over a bit
460         if (properties.isFairQueue()) {
461             idle = new FairBlockingQueue<>();
462             //idle = new MultiLockFairBlockingQueue<PooledConnection>();
463             //idle = new LinkedTransferQueue<PooledConnection>();
464             //idle = new ArrayBlockingQueue<PooledConnection>(properties.getMaxActive(),false);
465         } else {
466             idle = new LinkedBlockingQueue<>();
467         }
468
469         initializePoolCleaner(properties);
470
471         //create JMX MBean
472         if (this.getPoolProperties().isJmxEnabled()) createMBean();
473
474         //Parse and create an initial set of interceptors. Letting them know the pool has started.
475         //These interceptors will not get any connection.
476         PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray();
477         for (int i=0; i<proxies.length; i++) {
478             try {
479                 if (log.isDebugEnabled()) {
480                     log.debug("Creating interceptor instance of class:"+proxies[i].getInterceptorClass());
481                 }
482                 JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance();
483                 interceptor.setProperties(proxies[i].getProperties());
484                 interceptor.poolStarted(this);
485             }catch (Exception x) {
486                 log.error("Unable to inform interceptor of pool start.",x);
487                 if (jmxPool!=null) jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, getStackTrace(x));
488                 close(true);
489                 SQLException ex = new SQLException();
490                 ex.initCause(x);
491                 throw ex;
492             }
493         }
494
495         //initialize the pool with its initial set of members
496         PooledConnection[] initialPool = new PooledConnection[poolProperties.getInitialSize()];
497         try {
498             for (int i = 0; i < initialPool.length; i++) {
499                 initialPool[i] = this.borrowConnection(0, null, null); //don't wait, should be no contention
500             } //for
501
502         } catch (SQLException x) {
503             log.error("Unable to create initial connections of pool.", x);
504             if (!poolProperties.isIgnoreExceptionOnPreLoad()) {
505                 if (jmxPool!=null) jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, getStackTrace(x));
506                 close(true);
507                 throw x;
508             }
509         } finally {
510             //return the members as idle to the pool
511             for (int i = 0; i < initialPool.length; i++) {
512                 if (initialPool[i] != null) {
513                     try {this.returnConnection(initialPool[i]);}catch(Exception x){/*NOOP*/}
514                 } //end if
515             } //for
516         } //catch
517
518         closed = false;
519     }
520
521     public void checkPoolConfiguration(PoolConfiguration properties) {
522         //make sure the pool is properly configured
523         if (properties.getMaxActive()<1) {
524             log.warn("maxActive is smaller than 1, setting maxActive to: "+PoolProperties.DEFAULT_MAX_ACTIVE);
525             properties.setMaxActive(PoolProperties.DEFAULT_MAX_ACTIVE);
526         }
527         if (properties.getMaxActive()<properties.getInitialSize()) {
528             log.warn("initialSize is larger than maxActive, setting initialSize to: "+properties.getMaxActive());
529             properties.setInitialSize(properties.getMaxActive());
530         }
531         if (properties.getMinIdle()>properties.getMaxActive()) {
532             log.warn("minIdle is larger than maxActive, setting minIdle to: "+properties.getMaxActive());
533             properties.setMinIdle(properties.getMaxActive());
534         }
535         if (properties.getMaxIdle()>properties.getMaxActive()) {
536             log.warn("maxIdle is larger than maxActive, setting maxIdle to: "+properties.getMaxActive());
537             properties.setMaxIdle(properties.getMaxActive());
538         }
539         if (properties.getMaxIdle()<properties.getMinIdle()) {
540             log.warn("maxIdle is smaller than minIdle, setting maxIdle to: "+properties.getMinIdle());
541             properties.setMaxIdle(properties.getMinIdle());
542         }
543     }
544
545     public void initializePoolCleaner(PoolConfiguration properties) {
546         //if the evictor thread is supposed to run, start it now
547         if (properties.isPoolSweeperEnabled()) {
548             poolCleaner = new PoolCleaner(this, properties.getTimeBetweenEvictionRunsMillis());
549             poolCleaner.start();
550         } //end if
551     }
552
553     public void terminatePoolCleaner() {
554         if (poolCleaner!= null) {
555             poolCleaner.stopRunning();
556             poolCleaner = null;
557         }
558     }
559
560
561 //===============================================================================
562 //         CONNECTION POOLING IMPL LOGIC
563 //===============================================================================
564
565     /**
566      * thread safe way to abandon a connection
567      * signals a connection to be abandoned.
568      * this will disconnect the connection, and log the stack trace if logAbandoned=true
569      * @param con PooledConnection
570      */
571     protected void abandon(PooledConnection con) {
572         if (con == null)
573             return;
574         try {
575             con.lock();
576             String trace = con.getStackTrace();
577             if (getPoolProperties().isLogAbandoned()) {
578                 log.warn("Connection has been abandoned " + con + ":" + trace);
579             }
580             if (jmxPool!=null) {
581                 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_ABANDON, trace);
582             }
583             //release the connection
584             removeAbandonedCount.incrementAndGet();
585             release(con);
586         } finally {
587             con.unlock();
588         }
589     }
590
591     /**
592      * Thread safe way to suspect a connection. Similar to
593      * {@link #abandon(PooledConnection)}, but instead of actually abandoning
594      * the connection, this will log a warning and set the suspect flag on the
595      * {@link PooledConnection} if logAbandoned=true
596      *
597      * @param con PooledConnection
598      */
599     protected void suspect(PooledConnection con) {
600         if (con == null)
601             return;
602         if (con.isSuspect())
603             return;
604         try {
605             con.lock();
606             String trace = con.getStackTrace();
607             if (getPoolProperties().isLogAbandoned()) {
608                 log.warn("Connection has been marked suspect, possibly abandoned " + con + "["+(System.currentTimeMillis()-con.getTimestamp())+" ms.]:" + trace);
609             }
610             if (jmxPool!=null) {
611                 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_ABANDONED_NOTIFICATION, trace);
612             }
613             con.setSuspect(true);
614         } finally {
615             con.unlock();
616         }
617     }
618
619     /**
620      * thread safe way to release a connection
621      * @param con PooledConnection
622      */
623     protected void release(PooledConnection con) {
624         if (con == null)
625             return;
626         try {
627             con.lock();
628             if (con.release()) {
629                 //counter only decremented once
630                 size.addAndGet(-1);
631                 con.setHandler(null);
632             }
633             releasedCount.incrementAndGet();
634         } finally {
635             con.unlock();
636         }
637         // we've asynchronously reduced the number of connections
638         // we could have threads stuck in idle.poll(timeout) that will never be
639         // notified
640         if (waitcount.get() > 0) {
641             idle.offer(create(true));
642         }
643     }
644
645     /**
646      * Thread safe way to retrieve a connection from the pool
647      * @param wait - time to wait, overrides the maxWait from the properties,
648      * set to -1 if you wish to use maxWait, 0 if you wish no wait time.
649      * @param username The user name to use for the connection
650      * @param password The password for the connection
651      * @return a connection
652      * @throws SQLException Failed to get a connection
653      */
654     private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {
655
656         if (isClosed()) {
657             throw new SQLException("Connection pool closed.");
658         } //end if
659
660         //get the current time stamp
661         long now = System.currentTimeMillis();
662         //see if there is one available immediately
663         PooledConnection con = idle.poll();
664
665         while (true) {
666             if (con!=null) {
667                 //configure the connection and return it
668                 PooledConnection result = borrowConnection(now, con, username, password);
669                 borrowedCount.incrementAndGet();
670                 if (result!=null) return result;
671             }
672
673             //if we get here, see if we need to create one
674             //this is not 100% accurate since it doesn't use a shared
675             //atomic variable - a connection can become idle while we are creating
676             //a new connection
677             if (size.get() < getPoolProperties().getMaxActive()) {
678                 //atomic duplicate check
679                 if (size.addAndGet(1) > getPoolProperties().getMaxActive()) {
680                     //if we got here, two threads passed through the first if
681                     size.decrementAndGet();
682                 } else {
683                     //create a connection, we're below the limit
684                     return createConnection(now, con, username, password);
685                 }
686             } //end if
687
688             //calculate wait time for this iteration
689             long maxWait = wait;
690             //if the passed in wait time is -1, means we should use the pool property value
691             if (wait==-1) {
692                 maxWait = (getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait();
693             }
694
695             long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now));
696             waitcount.incrementAndGet();
697             try {
698                 //retrieve an existing connection
699                 con = idle.poll(timetowait, TimeUnit.MILLISECONDS);
700             } catch (InterruptedException ex) {
701                 if (getPoolProperties().getPropagateInterruptState()) {
702                     Thread.currentThread().interrupt();
703                 }
704                 SQLException sx = new SQLException("Pool wait interrupted.");
705                 sx.initCause(ex);
706                 throw sx;
707             } finally {
708                 waitcount.decrementAndGet();
709             }
710             if (maxWait==0 && con == null) { //no wait, return one if we have one
711                 if (jmxPool!=null) {
712                     jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - no wait.");
713                 }
714                 throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
715                         "NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use].");
716             }
717             //we didn't get a connection, lets see if we timed out
718             if (con == null) {
719                 if ((System.currentTimeMillis() - now) >= maxWait) {
720                     if (jmxPool!=null) {
721                         jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout.");
722                     }
723                     throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
724                         "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) +
725                         " seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"].");
726                 } else {
727                     //no timeout, lets try again
728                     continue;
729                 }
730             }
731         } //while
732     }
733
734     /**
735      * Creates a JDBC connection and tries to connect to the database.
736      * @param now timestamp of when this was called
737      * @param notUsed Argument not used
738      * @param username The user name to use for the connection
739      * @param password The password for the connection
740      * @return a PooledConnection that has been connected
741      * @throws SQLException Failed to get a connection
742      */
743     protected PooledConnection createConnection(long now, PooledConnection notUsed, String username, String password) throws SQLException {
744         //no connections where available we'll create one
745         PooledConnection con = create(false);
746         if (username!=null) con.getAttributes().put(PooledConnection.PROP_USER, username);
747         if (password!=null) con.getAttributes().put(PooledConnection.PROP_PASSWORD, password);
748         boolean error = false;
749         try {
750             //connect and validate the connection
751             con.lock();
752             con.connect();
753             if (con.validate(PooledConnection.VALIDATE_INIT)) {
754                 //no need to lock a new one, its not contented
755                 con.setTimestamp(now);
756                 if (getPoolProperties().isLogAbandoned()) {
757                     con.setStackTrace(getThreadDump());
758                 }
759                 if (!busy.offer(con)) {
760                     log.debug("Connection doesn't fit into busy array, connection will not be traceable.");
761                 }
762                 createdCount.incrementAndGet();
763                 return con;
764             } else {
765                 //validation failed, make sure we disconnect
766                 //and clean up
767                 throw new SQLException("Validation Query Failed, enable logValidationErrors for more details.");
768             } //end if
769         } catch (Exception e) {
770             error = true;
771             if (log.isDebugEnabled())
772                 log.debug("Unable to create a new JDBC connection.", e);
773             if (e instanceof SQLException) {
774                 throw (SQLException)e;
775             } else {
776                 SQLException ex = new SQLException(e.getMessage());
777                 ex.initCause(e);
778                 throw ex;
779             }
780         } finally {
781             // con can never be null here
782             if (error ) {
783                 release(con);
784             }
785             con.unlock();
786         }//catch
787     }
788
789     /**
790      * Validates and configures a previously idle connection
791      * @param now - timestamp
792      * @param con - the connection to validate and configure
793      * @param username The user name to use for the connection
794      * @param password The password for the connection
795      * @return a connection
796      * @throws SQLException if a validation error happens
797      */
798     protected PooledConnection borrowConnection(long now, PooledConnection con, String username, String password) throws SQLException {
799         //we have a connection, lets set it up
800
801         //flag to see if we need to nullify
802         boolean setToNull = false;
803         try {
804             con.lock();
805             if (con.isReleased()) {
806                 return null;
807             }
808
809             //evaluate username/password change as well as max age functionality
810             boolean forceReconnect = con.shouldForceReconnect(username, password) || con.isMaxAgeExpired();
811
812             if (!con.isDiscarded() && !con.isInitialized()) {
813                 //here it states that the connection not discarded, but the connection is null
814                 //don't attempt a connect here. It will be done during the reconnect.
815                 forceReconnect = true;
816             }
817
818             if (!forceReconnect) {
819                 if ((!con.isDiscarded()) && con.validate(PooledConnection.VALIDATE_BORROW)) {
820                     //set the timestamp
821                     con.setTimestamp(now);
822                     if (getPoolProperties().isLogAbandoned()) {
823                         //set the stack trace for this pool
824                         con.setStackTrace(getThreadDump());
825                     }
826                     if (!busy.offer(con)) {
827                         log.debug("Connection doesn't fit into busy array, connection will not be traceable.");
828                     }
829                     return con;
830                 }
831             }
832             //if we reached here, that means the connection
833             //is either has another principal, is discarded or validation failed.
834             //we will make one more attempt
835             //in order to guarantee that the thread that just acquired
836             //the connection shouldn't have to poll again.
837             try {
838                 con.reconnect();
839                 reconnectedCount.incrementAndGet();
840                 int validationMode = getPoolProperties().isTestOnConnect() || getPoolProperties().getInitSQL()!=null ?
841                     PooledConnection.VALIDATE_INIT :
842                     PooledConnection.VALIDATE_BORROW;
843
844                 if (con.validate(validationMode)) {
845                     //set the timestamp
846                     con.setTimestamp(now);
847                     if (getPoolProperties().isLogAbandoned()) {
848                         //set the stack trace for this pool
849                         con.setStackTrace(getThreadDump());
850                     }
851                     if (!busy.offer(con)) {
852                         log.debug("Connection doesn't fit into busy array, connection will not be traceable.");
853                     }
854                     return con;
855                 } else {
856                     //validation failed.
857                     throw new SQLException("Failed to validate a newly established connection.");
858                 }
859             } catch (Exception x) {
860                 release(con);
861                 setToNull = true;
862                 if (x instanceof SQLException) {
863                     throw (SQLException)x;
864                 } else {
865                     SQLException ex  = new SQLException(x.getMessage());
866                     ex.initCause(x);
867                     throw ex;
868                 }
869             }
870         } finally {
871             con.unlock();
872             if (setToNull) {
873                 con = null;
874             }
875         }
876     }
877     /**
878      * Terminate the current transaction for the given connection.
879      * @param con The connection
880      * @return <code>true</code> if the connection TX termination succeeded
881      *         otherwise <code>false</code>
882      */
883     protected boolean terminateTransaction(PooledConnection con) {
884         try {
885             if (Boolean.FALSE.equals(con.getPoolProperties().getDefaultAutoCommit())) {
886                 if (this.getPoolProperties().getRollbackOnReturn()) {
887                     boolean autocommit = con.getConnection().getAutoCommit();
888                     if (!autocommit) con.getConnection().rollback();
889                 } else if (this.getPoolProperties().getCommitOnReturn()) {
890                     boolean autocommit = con.getConnection().getAutoCommit();
891                     if (!autocommit) con.getConnection().commit();
892                 }
893             }
894             return true;
895         } catch (SQLException x) {
896             log.warn("Unable to terminate transaction, connection will be closed.",x);
897             return false;
898         }
899
900     }
901
902     /**
903      * Determines if a connection should be closed upon return to the pool.
904      * @param con - the connection
905      * @param action - the validation action that should be performed
906      * @return <code>true</code> if the connection should be closed
907      */
908     protected boolean shouldClose(PooledConnection con, int action) {
909         if (con.getConnectionVersion() < getPoolVersion()) return true;
910         if (con.isDiscarded()) return true;
911         if (isClosed()) return true;
912         if (!con.validate(action)) return true;
913         if (!terminateTransaction(con)) return true;
914         if (con.isMaxAgeExpired()) return true;
915         else return false;
916     }
917
918     /**
919      * Returns a connection to the pool
920      * If the pool is closed, the connection will be released
921      * If the connection is not part of the busy queue, it will be released.
922      * If {@link PoolProperties#testOnReturn} is set to true it will be validated
923      * @param con PooledConnection to be returned to the pool
924      */
925     protected void returnConnection(PooledConnection con) {
926         if (isClosed()) {
927             //if the connection pool is closed
928             //close the connection instead of returning it
929             release(con);
930             return;
931         } //end if
932
933         if (con != null) {
934             try {
935                 returnedCount.incrementAndGet();
936                 con.lock();
937                 if (con.isSuspect()) {
938                     if (poolProperties.isLogAbandoned() && log.isInfoEnabled()) {
939                         log.info("Connection(" + con + ") that has been marked suspect was returned."
940                                 + " The processing time is " + (System.currentTimeMillis()-con.getTimestamp()) + " ms.");
941                     }
942                     if (jmxPool!=null) {
943                         jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_RETURNED_NOTIFICATION,
944                                 "Connection(" + con + ") that has been marked suspect was returned.");
945                     }
946                 }
947                 if (busy.remove(con)) {
948
949                     if (!shouldClose(con,PooledConnection.VALIDATE_RETURN)) {
950                         con.setStackTrace(null);
951                         con.setTimestamp(System.currentTimeMillis());
952                         if (((idle.size()>=poolProperties.getMaxIdle()) && !poolProperties.isPoolSweeperEnabled()) || (!idle.offer(con))) {
953                             if (log.isDebugEnabled()) {
954                                 log.debug("Connection ["+con+"] will be closed and not returned to the pool, idle["+idle.size()+"]>=maxIdle["+poolProperties.getMaxIdle()+"] idle.offer failed.");
955                             }
956                             release(con);
957                         }
958                     } else {
959                         if (log.isDebugEnabled()) {
960                             log.debug("Connection ["+con+"] will be closed and not returned to the pool.");
961                         }
962                         release(con);
963                     } //end if
964                 } else {
965                     if (log.isDebugEnabled()) {
966                         log.debug("Connection ["+con+"] will be closed and not returned to the pool, busy.remove failed.");
967                     }
968                     release(con);
969                 }
970             } finally {
971                 con.unlock();
972             }
973         } //end if
974     } //checkIn
975
976     /**
977      * Determines if a connection should be abandoned based on
978      * {@link PoolProperties#abandonWhenPercentageFull} setting.
979      * @return <code>true</code> if the connection should be abandoned
980      */
981     protected boolean shouldAbandon() {
982         if (!poolProperties.isRemoveAbandoned()) return false;
983         if (poolProperties.getAbandonWhenPercentageFull()==0) return true;
984         float used = busy.size();
985         float max  = poolProperties.getMaxActive();
986         float perc = poolProperties.getAbandonWhenPercentageFull();
987         return (used/max*100f)>=perc;
988     }
989
990     /**
991      * Iterates through all the busy connections and checks for connections that have timed out
992      */
993     public void checkAbandoned() {
994         try {
995             if (busy.size()==0) return;
996             Iterator<PooledConnection> locked = busy.iterator();
997             int sto = getPoolProperties().getSuspectTimeout();
998             while (locked.hasNext()) {
999                 PooledConnection con = locked.next();
1000                 boolean setToNull = false;
1001                 try {
1002                     con.lock();
1003                     //the con has been returned to the pool or released
1004                     //ignore it
1005                     if (idle.contains(con) || con.isReleased())
1006                         continue;
1007                     long time = con.getTimestamp();
1008                     long now = System.currentTimeMillis();
1009                     if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) {
1010                         busy.remove(con);
1011                         abandon(con);
1012                         setToNull = true;
1013                     } else if (sto > 0 && (now - time) > (sto * 1000L)) {
1014                         suspect(con);
1015                     } else {
1016                         //do nothing
1017                     } //end if
1018                 } finally {
1019                     con.unlock();
1020                     if (setToNull)
1021                         con = null;
1022                 }
1023             } //while
1024         } catch (ConcurrentModificationException e) {
1025             log.debug("checkAbandoned failed." ,e);
1026         } catch (Exception e) {
1027             log.warn("checkAbandoned failed, it will be retried.",e);
1028         }
1029     }
1030
1031     /**
1032      * Iterates through the idle connections and resizes the idle pool based on parameters
1033      * {@link PoolProperties#maxIdle}, {@link PoolProperties#minIdle}, {@link PoolProperties#minEvictableIdleTimeMillis}
1034      */
1035     public void checkIdle() {
1036         checkIdle(false);
1037     }
1038
1039     public void checkIdle(boolean ignoreMinSize) {
1040
1041         try {
1042             if (idle.size()==0) return;
1043             long now = System.currentTimeMillis();
1044             Iterator<PooledConnection> unlocked = idle.iterator();
1045             while ( (ignoreMinSize || (idle.size()>=getPoolProperties().getMinIdle())) && unlocked.hasNext()) {
1046                 PooledConnection con = unlocked.next();
1047                 boolean setToNull = false;
1048                 try {
1049                     con.lock();
1050                     //the con been taken out, we can't clean it up
1051                     if (busy.contains(con))
1052                         continue;
1053                     long time = con.getTimestamp();
1054                     if (shouldReleaseIdle(now, con, time)) {
1055                         releasedIdleCount.incrementAndGet();
1056                         release(con);
1057                         idle.remove(con);
1058                         setToNull = true;
1059                     } else {
1060                         //do nothing
1061                     } //end if
1062                 } finally {
1063                     con.unlock();
1064                     if (setToNull)
1065                         con = null;
1066                 }
1067             } //while
1068         } catch (ConcurrentModificationException e) {
1069             log.debug("checkIdle failed." ,e);
1070         } catch (Exception e) {
1071             log.warn("checkIdle failed, it will be retried.",e);
1072         }
1073
1074     }
1075
1076
1077     protected boolean shouldReleaseIdle(long now, PooledConnection con, long time) {
1078         if (con.getConnectionVersion() < getPoolVersion()) return true;
1079         else return (con.getReleaseTime()>0) && ((now - time) > con.getReleaseTime()) && (getSize()>getPoolProperties().getMinIdle());
1080     }
1081
1082     /**
1083      * Forces a validation of all idle connections if {@link PoolProperties#testWhileIdle} is set.
1084      */
1085     public void testAllIdle() {
1086         try {
1087             if (idle.size()==0) return;
1088             Iterator<PooledConnection> unlocked = idle.iterator();
1089             while (unlocked.hasNext()) {
1090                 PooledConnection con = unlocked.next();
1091                 try {
1092                     con.lock();
1093                     //the con been taken out, we can't clean it up
1094                     if (busy.contains(con))
1095                         continue;
1096                     if (!con.validate(PooledConnection.VALIDATE_IDLE)) {
1097                         idle.remove(con);
1098                         release(con);
1099                     }
1100                 } finally {
1101                     con.unlock();
1102                 }
1103             } //while
1104         } catch (ConcurrentModificationException e) {
1105             log.debug("testAllIdle failed." ,e);
1106         } catch (Exception e) {
1107             log.warn("testAllIdle failed, it will be retried.",e);
1108         }
1109
1110     }
1111
1112     /**
1113      * Creates a stack trace representing the existing thread's current state.
1114      * @return a string object representing the current state.
1115      * TODO investigate if we simply should store {@link java.lang.Thread#getStackTrace()} elements
1116      */
1117     protected static String getThreadDump() {
1118         Exception x = new Exception();
1119         x.fillInStackTrace();
1120         return getStackTrace(x);
1121     }
1122
1123     /**
1124      * Convert an exception into a String
1125      * @param x - the throwable
1126      * @return a string representing the stack trace
1127      */
1128     public static String getStackTrace(Throwable x) {
1129         if (x == null) {
1130             return null;
1131         } else {
1132             java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
1133             java.io.PrintStream writer = new java.io.PrintStream(bout);
1134             x.printStackTrace(writer);
1135             String result = bout.toString();
1136             return (x.getMessage()!=null && x.getMessage().length()>0)? x.getMessage()+";"+result:result;
1137         } //end if
1138     }
1139
1140
1141     /**
1142      * Create a new pooled connection object. Not connected nor validated.
1143      * @param incrementCounter <code>true</code> to increment the connection count
1144      * @return a pooled connection object
1145      */
1146     protected PooledConnection create(boolean incrementCounter) {
1147         if (incrementCounter) size.incrementAndGet();
1148         PooledConnection con = new PooledConnection(getPoolProperties(), this);
1149         return con;
1150     }
1151
1152     /**
1153      * Purges all connections in the pool.
1154      * For connections currently in use, these connections will be
1155      * purged when returned on the pool. This call also
1156      * purges connections that are idle and in the pool
1157      * To only purge used/active connections see {@link #purgeOnReturn()}
1158      */
1159     public void purge() {
1160         purgeOnReturn();
1161         checkIdle(true);
1162     }
1163
1164     /**
1165      * Purges connections when they are returned from the pool.
1166      * This call does not purge idle connections until they are used.
1167      * To purge idle connections see {@link #purge()}
1168      */
1169     public void purgeOnReturn() {
1170         poolVersion.incrementAndGet();
1171     }
1172
1173     /**
1174      * Hook to perform final actions on a pooled connection object once it has been disconnected and will be discarded
1175      * @param con The connection
1176      */
1177     protected void finalize(PooledConnection con) {
1178         JdbcInterceptor handler = con.getHandler();
1179         while (handler!=null) {
1180             handler.reset(null, null);
1181             handler=handler.getNext();
1182         }
1183     }
1184
1185     /**
1186      * Hook to perform final actions on a pooled connection object once it has been disconnected and will be discarded
1187      * @param con The connection
1188      * @param finalizing <code>true</code> if finalizing the connection
1189      */
1190     protected void disconnectEvent(PooledConnection con, boolean finalizing) {
1191         JdbcInterceptor handler = con.getHandler();
1192         while (handler!=null) {
1193             handler.disconnected(this, con, finalizing);
1194             handler=handler.getNext();
1195         }
1196     }
1197
1198     /**
1199      * Return the object that is potentially registered in JMX for notifications
1200      * @return the object implementing the {@link org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} interface
1201      */
1202     public org.apache.tomcat.jdbc.pool.jmx.ConnectionPool getJmxPool() {
1203         return jmxPool;
1204     }
1205
1206     /**
1207      * Create MBean object that can be registered.
1208      */
1209     protected void createMBean() {
1210         try {
1211             jmxPool = new org.apache.tomcat.jdbc.pool.jmx.ConnectionPool(this);
1212         } catch (Exception x) {
1213             log.warn("Unable to start JMX integration for connection pool. Instance["+getName()+"] can't be monitored.",x);
1214         }
1215     }
1216
1217     /**
1218      * The total number of connections borrowed from this pool.
1219      * @return the borrowed connection count
1220      */
1221     public long getBorrowedCount() {
1222         return borrowedCount.get();
1223     }
1224
1225     /**
1226      * The total number of connections returned to this pool.
1227      * @return the returned connection count
1228      */
1229     public long getReturnedCount() {
1230         return returnedCount.get();
1231     }
1232
1233     /**
1234      * The total number of connections created by this pool.
1235      * @return the created connection count
1236      */
1237     public long getCreatedCount() {
1238         return createdCount.get();
1239     }
1240
1241     /**
1242      * The total number of connections released from this pool.
1243      * @return the released connection count
1244      */
1245     public long getReleasedCount() {
1246         return releasedCount.get();
1247     }
1248
1249     /**
1250      * The total number of connections reconnected by this pool.
1251      * @return the reconnected connection count
1252      */
1253     public long getReconnectedCount() {
1254         return reconnectedCount.get();
1255     }
1256
1257     /**
1258      * The total number of connections released by remove abandoned.
1259      * @return the PoolCleaner removed abandoned connection count
1260      */
1261     public long getRemoveAbandonedCount() {
1262         return removeAbandonedCount.get();
1263     }
1264
1265     /**
1266      * The total number of connections released by eviction.
1267      * @return the PoolCleaner evicted idle connection count
1268      */
1269     public long getReleasedIdleCount() {
1270         return releasedIdleCount.get();
1271     }
1272
1273     /**
1274      * reset the statistics of this pool.
1275      */
1276     public void resetStats() {
1277         borrowedCount.set(0);
1278         returnedCount.set(0);
1279         createdCount.set(0);
1280         releasedCount.set(0);
1281         reconnectedCount.set(0);
1282         removeAbandonedCount.set(0);
1283         releasedIdleCount.set(0);
1284     }
1285
1286     /**
1287      * Tread safe wrapper around a future for the regular queue
1288      * This one retrieves the pooled connection object
1289      * and performs the initialization according to
1290      * interceptors and validation rules.
1291      * This class is thread safe and is cancellable
1292      *
1293      */
1294     protected class ConnectionFuture implements Future<Connection>, Runnable {
1295         Future<PooledConnection> pcFuture = null;
1296         AtomicBoolean configured = new AtomicBoolean(false);
1297         CountDownLatch latch = new CountDownLatch(1);
1298         volatile Connection result = null;
1299         SQLException cause = null;
1300         AtomicBoolean cancelled = new AtomicBoolean(false);
1301         volatile PooledConnection pc = null;
1302         public ConnectionFuture(Future<PooledConnection> pcf) {
1303             this.pcFuture = pcf;
1304         }
1305
1306         public ConnectionFuture(PooledConnection pc) throws SQLException {
1307             this.pc = pc;
1308             result = ConnectionPool.this.setupConnection(pc);
1309             configured.set(true);
1310         }
1311         /**
1312          * {@inheritDoc}
1313          */
1314         @Override
1315         public boolean cancel(boolean mayInterruptIfRunning) {
1316             if (pc!=null) {
1317                 return false;
1318             } else if ((!cancelled.get()) && cancelled.compareAndSet(false, true)) {
1319                 //cancel by retrieving the connection and returning it to the pool
1320                 ConnectionPool.this.cancellator.execute(this);
1321             }
1322             return true;
1323         }
1324
1325         /**
1326          * {@inheritDoc}
1327          */
1328         @Override
1329         public Connection get() throws InterruptedException, ExecutionException {
1330             try {
1331                 return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
1332             }catch (TimeoutException x) {
1333                 throw new ExecutionException(x);
1334             }
1335         }
1336
1337         /**
1338          * {@inheritDoc}
1339          */
1340         @Override
1341         public Connection get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
1342             PooledConnection pc = this.pc!=null?this.pc:pcFuture.get(timeout,unit);
1343             if (pc!=null) {
1344                 if (result!=null) return result;
1345                 if (configured.compareAndSet(false, true)) {
1346                     try {
1347                         pc = borrowConnection(System.currentTimeMillis(),pc, null, null);
1348                         result = ConnectionPool.this.setupConnection(pc);
1349                     } catch (SQLException x) {
1350                         cause = x;
1351                     } finally {
1352                         latch.countDown();
1353                     }
1354                 } else {
1355                     //if we reach here, another thread is configuring the actual connection
1356                     latch.await(timeout,unit); //this shouldn't block for long
1357                 }
1358                 if (result==null) throw new ExecutionException(cause);
1359                 return result;
1360             } else {
1361                 return null;
1362             }
1363         }
1364
1365         /**
1366          * {@inheritDoc}
1367          */
1368         @Override
1369         public boolean isCancelled() {
1370             return pc==null && (pcFuture.isCancelled() || cancelled.get());
1371         }
1372
1373         /**
1374          * {@inheritDoc}
1375          */
1376         @Override
1377         public boolean isDone() {
1378             return pc!=null || pcFuture.isDone();
1379         }
1380
1381         /**
1382          * run method to be executed when cancelled by an executor
1383          */
1384         @Override
1385         public void run() {
1386             try {
1387                 Connection con = get(); //complete this future
1388                 con.close(); //return to the pool
1389             }catch (ExecutionException ex) {
1390                 //we can ignore this
1391             }catch (Exception x) {
1392                 ConnectionPool.log.error("Unable to cancel ConnectionFuture.",x);
1393             }
1394         }
1395
1396     }
1397
1398
1399
1400     private static volatile Timer poolCleanTimer = null;
1401     private static HashSet<PoolCleaner> cleaners = new HashSet<>();
1402
1403     private static synchronized void registerCleaner(PoolCleaner cleaner) {
1404         unregisterCleaner(cleaner);
1405         cleaners.add(cleaner);
1406         if (poolCleanTimer == null) {
1407             ClassLoader loader = Thread.currentThread().getContextClassLoader();
1408             try {
1409                 Thread.currentThread().setContextClassLoader(ConnectionPool.class.getClassLoader());
1410                 // Create the timer thread in a PrivilegedAction so that a
1411                 // reference to the web application class loader is not created
1412                 // via Thread.inheritedAccessControlContext
1413                 PrivilegedAction<Timer> pa = new PrivilegedNewTimer();
1414                 poolCleanTimer = AccessController.doPrivileged(pa);
1415             } finally {
1416                 Thread.currentThread().setContextClassLoader(loader);
1417             }
1418         }
1419         poolCleanTimer.schedule(cleaner, cleaner.sleepTime,cleaner.sleepTime);
1420     }
1421
1422     private static synchronized void unregisterCleaner(PoolCleaner cleaner) {
1423         boolean removed = cleaners.remove(cleaner);
1424         if (removed) {
1425             cleaner.cancel();
1426             if (poolCleanTimer != null) {
1427                 poolCleanTimer.purge();
1428                 if (cleaners.size() == 0) {
1429                     poolCleanTimer.cancel();
1430                     poolCleanTimer = null;
1431                 }
1432             }
1433         }
1434     }
1435
1436     private static class PrivilegedNewTimer implements PrivilegedAction<Timer> {
1437         @Override
1438         public Timer run() {
1439             return new Timer("Tomcat JDBC Pool Cleaner["+ System.identityHashCode(ConnectionPool.class.getClassLoader()) + ":"+
1440                     System.currentTimeMillis() + "]", true);
1441         }
1442     }
1443
1444     public static Set<TimerTask> getPoolCleaners() {
1445         return Collections.<TimerTask>unmodifiableSet(cleaners);
1446     }
1447
1448     public long getPoolVersion() {
1449         return poolVersion.get();
1450     }
1451
1452     public static Timer getPoolTimer() {
1453         return poolCleanTimer;
1454     }
1455
1456     protected static class PoolCleaner extends TimerTask {
1457         protected WeakReference<ConnectionPool> pool;
1458         protected long sleepTime;
1459
1460         PoolCleaner(ConnectionPool pool, long sleepTime) {
1461             this.pool = new WeakReference<>(pool);
1462             this.sleepTime = sleepTime;
1463             if (sleepTime <= 0) {
1464                 log.warn("Database connection pool evicter thread interval is set to 0, defaulting to 30 seconds");
1465                 this.sleepTime = 1000 * 30;
1466             } else if (sleepTime < 1000) {
1467                 log.warn("Database connection pool evicter thread interval is set to lower than 1 second.");
1468             }
1469         }
1470
1471         @Override
1472         public void run() {
1473             ConnectionPool pool = this.pool.get();
1474             if (pool == null) {
1475                 stopRunning();
1476             } else if (!pool.isClosed()) {
1477                 try {
1478                     if (pool.getPoolProperties().isRemoveAbandoned()
1479                             || pool.getPoolProperties().getSuspectTimeout() > 0)
1480                         pool.checkAbandoned();
1481                     if (pool.getPoolProperties().getMinIdle() < pool.idle
1482                             .size())
1483                         pool.checkIdle();
1484                     if (pool.getPoolProperties().isTestWhileIdle())
1485                         pool.testAllIdle();
1486                 } catch (Exception x) {
1487                     log.error("", x);
1488                 }
1489             }
1490         }
1491
1492         public void start() {
1493             registerCleaner(this);
1494         }
1495
1496         public void stopRunning() {
1497             unregisterCleaner(this);
1498         }
1499     }
1500 }