2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
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
29 * http://www.apache.org/licenses/LICENSE-2.0
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.
37 package org.apache.tomcat.jdbc.pool;
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;
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;
66 import org.apache.juli.logging.Log;
67 import org.apache.juli.logging.LogFactory;
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.
76 public class ConnectionPool {
79 * Default domain for objects registering with an mbean server
81 public static final String POOL_JMX_DOMAIN = "tomcat.jdbc";
83 * Prefix type for JMX registration
85 public static final String POOL_JMX_TYPE_PREFIX = POOL_JMX_DOMAIN+":type=";
90 private static final Log log = LogFactory.getLog(ConnectionPool.class);
92 //===============================================================================
93 // INSTANCE/QUICK ACCESS VARIABLE
94 //===============================================================================
96 * Carries the size of the pool, instead of relying on a queue implementation
97 * that usually iterates over to get an exact count
99 private AtomicInteger size = new AtomicInteger(0);
102 * All the information about the connection pool
103 * These are the properties the pool got instantiated with
105 private PoolConfiguration poolProperties;
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
111 private BlockingQueue<PooledConnection> busy;
114 * Contains all the idle connections
116 private BlockingQueue<PooledConnection> idle;
119 * The thread that is responsible for checking abandoned and idle threads
121 private volatile PoolCleaner poolCleaner;
126 private volatile boolean closed = false;
129 * Since newProxyInstance performs the same operation, over and over
130 * again, it is much more optimized if we simply store the constructor ourselves.
132 private Constructor<?> proxyClassConstructor;
135 * Executor service used to cancel Futures
137 private ThreadPoolExecutor cancellator = new ThreadPoolExecutor(0,1,1000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
140 * reference to the JMX mbean
142 protected org.apache.tomcat.jdbc.pool.jmx.ConnectionPool jmxPool = null;
145 * counter to track how many threads are waiting for a connection
147 private AtomicInteger waitcount = new AtomicInteger(0);
149 private AtomicLong poolVersion = new AtomicLong(Long.MIN_VALUE);
152 * The counters for statistics of the pool.
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);
162 //===============================================================================
164 //===============================================================================
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
172 public ConnectionPool(PoolConfiguration prop) throws SQLException {
173 //setup quick access variables and pools
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
183 * @return a Future containing a reference to the connection or the future connection
184 * @throws SQLException Cannot use asynchronous connect
186 public Future<Connection> getConnectionAsync() throws SQLException {
188 PooledConnection pc = borrowConnection(0, null, null);
190 return new ConnectionFuture(pc);
192 }catch (SQLException x) {
193 if (x.getMessage().indexOf("NoWait")<0) {
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);
205 throw new SQLException("Connection pool is misconfigured, doesn't support async retrieval. Set the 'fair' property to 'true'");
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
216 public Connection getConnection() throws SQLException {
217 //check out a connection
218 PooledConnection con = borrowConnection(-1,null,null);
219 return setupConnection(con);
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
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);
244 * Returns the name of this pool
245 * @return String - the name of the pool
247 public String getName() {
248 return getPoolProperties().getPoolName();
252 * Return the number of threads waiting for a connection
253 * @return number of threads waiting for a connection
255 public int getWaitCount() {
256 return waitcount.get();
260 * Returns the pool properties associated with this connection pool
261 * @return PoolProperties
264 public PoolConfiguration getPoolProperties() {
265 return this.poolProperties;
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
272 public int getSize() {
277 * Returns the number of connections that are in use
278 * @return int - number of established connections that are being used by the application
280 public int getActive() {
285 * Returns the number of idle connections
286 * @return int - number of established connections not being used
288 public int getIdle() {
293 * Returns true if {@link #close close} has been called, and the connection pool is unusable
296 public boolean isClosed() {
300 //===============================================================================
302 //===============================================================================
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
314 protected Connection setupConnection(PooledConnection con) throws SQLException {
315 //fetch previously cached interceptor proxy - one per connection
316 JdbcInterceptor handler = con.getHandler();
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--) {
324 //create a new instance
325 JdbcInterceptor interceptor = proxies[i].getInterceptorClass().newInstance();
326 //configure properties
327 interceptor.setProperties(proxies[i].getProperties());
329 interceptor.setNext(handler);
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.");
340 //cache handler for the next iteration
341 con.setHandler(handler);
343 JdbcInterceptor next = handler;
344 //we have a cached handler, reset it
346 next.reset(this, con);
347 next = next.getNext();
352 getProxyConstructor(con.getXAConnection() != null);
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) });
359 connection = (Connection)proxyClassConstructor.newInstance(new Object[] {handler});
361 //return the connection
363 }catch (Exception x) {
364 SQLException s = new SQLException();
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
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 });
386 return proxyClassConstructor;
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
395 protected void close(boolean force) {
396 //are we already closed
397 if (this.closed) return;
398 //prevent other threads from entering
400 //stop background thread
401 if (poolCleaner!=null) {
402 poolCleaner.stopRunning();
405 /* release all idle connections */
406 BlockingQueue<PooledConnection> pool = (idle.size()>0)?idle:(force?busy:idle);
407 while (pool.size()>0) {
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
419 con = pool.poll(1000, TimeUnit.MILLISECONDS);
424 } catch (InterruptedException ex) {
425 if (getPoolProperties().getPropagateInterruptState()) {
426 Thread.currentThread().interrupt();
429 if (pool.size()==0 && force && pool!=busy) pool = busy;
431 if (this.getPoolProperties().isJmxEnabled()) this.jmxPool = null;
432 PoolProperties.InterceptorDefinition[] proxies = getPoolProperties().getJdbcInterceptorsAsArray();
433 for (int i=0; i<proxies.length; i++) {
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);
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
450 protected void init(PoolConfiguration properties) throws SQLException {
451 poolProperties = properties;
453 //make sure the pool is properly configured
454 checkPoolConfiguration(properties);
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);
466 idle = new LinkedBlockingQueue<>();
469 initializePoolCleaner(properties);
472 if (this.getPoolProperties().isJmxEnabled()) createMBean();
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++) {
479 if (log.isDebugEnabled()) {
480 log.debug("Creating interceptor instance of class:"+proxies[i].getInterceptorClass());
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));
489 SQLException ex = new SQLException();
495 //initialize the pool with its initial set of members
496 PooledConnection[] initialPool = new PooledConnection[poolProperties.getInitialSize()];
498 for (int i = 0; i < initialPool.length; i++) {
499 initialPool[i] = this.borrowConnection(0, null, null); //don't wait, should be no contention
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));
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*/}
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);
527 if (properties.getMaxActive()<properties.getInitialSize()) {
528 log.warn("initialSize is larger than maxActive, setting initialSize to: "+properties.getMaxActive());
529 properties.setInitialSize(properties.getMaxActive());
531 if (properties.getMinIdle()>properties.getMaxActive()) {
532 log.warn("minIdle is larger than maxActive, setting minIdle to: "+properties.getMaxActive());
533 properties.setMinIdle(properties.getMaxActive());
535 if (properties.getMaxIdle()>properties.getMaxActive()) {
536 log.warn("maxIdle is larger than maxActive, setting maxIdle to: "+properties.getMaxActive());
537 properties.setMaxIdle(properties.getMaxActive());
539 if (properties.getMaxIdle()<properties.getMinIdle()) {
540 log.warn("maxIdle is smaller than minIdle, setting maxIdle to: "+properties.getMinIdle());
541 properties.setMaxIdle(properties.getMinIdle());
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());
553 public void terminatePoolCleaner() {
554 if (poolCleaner!= null) {
555 poolCleaner.stopRunning();
561 //===============================================================================
562 // CONNECTION POOLING IMPL LOGIC
563 //===============================================================================
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
571 protected void abandon(PooledConnection con) {
576 String trace = con.getStackTrace();
577 if (getPoolProperties().isLogAbandoned()) {
578 log.warn("Connection has been abandoned " + con + ":" + trace);
581 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_ABANDON, trace);
583 //release the connection
584 removeAbandonedCount.incrementAndGet();
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
597 * @param con PooledConnection
599 protected void suspect(PooledConnection con) {
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);
611 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_ABANDONED_NOTIFICATION, trace);
613 con.setSuspect(true);
620 * thread safe way to release a connection
621 * @param con PooledConnection
623 protected void release(PooledConnection con) {
629 //counter only decremented once
631 con.setHandler(null);
633 releasedCount.incrementAndGet();
637 // we've asynchronously reduced the number of connections
638 // we could have threads stuck in idle.poll(timeout) that will never be
640 if (waitcount.get() > 0) {
641 idle.offer(create(true));
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
654 private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {
657 throw new SQLException("Connection pool closed.");
660 //get the current time stamp
661 long now = System.currentTimeMillis();
662 //see if there is one available immediately
663 PooledConnection con = idle.poll();
667 //configure the connection and return it
668 PooledConnection result = borrowConnection(now, con, username, password);
669 borrowedCount.incrementAndGet();
670 if (result!=null) return result;
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
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();
683 //create a connection, we're below the limit
684 return createConnection(now, con, username, password);
688 //calculate wait time for this iteration
690 //if the passed in wait time is -1, means we should use the pool property value
692 maxWait = (getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait();
695 long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now));
696 waitcount.incrementAndGet();
698 //retrieve an existing connection
699 con = idle.poll(timetowait, TimeUnit.MILLISECONDS);
700 } catch (InterruptedException ex) {
701 if (getPoolProperties().getPropagateInterruptState()) {
702 Thread.currentThread().interrupt();
704 SQLException sx = new SQLException("Pool wait interrupted.");
708 waitcount.decrementAndGet();
710 if (maxWait==0 && con == null) { //no wait, return one if we have one
712 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - no wait.");
714 throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
715 "NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use].");
717 //we didn't get a connection, lets see if we timed out
719 if ((System.currentTimeMillis() - now) >= maxWait) {
721 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout.");
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+"].");
727 //no timeout, lets try again
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
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;
750 //connect and validate the connection
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());
759 if (!busy.offer(con)) {
760 log.debug("Connection doesn't fit into busy array, connection will not be traceable.");
762 createdCount.incrementAndGet();
765 //validation failed, make sure we disconnect
767 throw new SQLException("Validation Query Failed, enable logValidationErrors for more details.");
769 } catch (Exception e) {
771 if (log.isDebugEnabled())
772 log.debug("Unable to create a new JDBC connection.", e);
773 if (e instanceof SQLException) {
774 throw (SQLException)e;
776 SQLException ex = new SQLException(e.getMessage());
781 // con can never be null here
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
798 protected PooledConnection borrowConnection(long now, PooledConnection con, String username, String password) throws SQLException {
799 //we have a connection, lets set it up
801 //flag to see if we need to nullify
802 boolean setToNull = false;
805 if (con.isReleased()) {
809 //evaluate username/password change as well as max age functionality
810 boolean forceReconnect = con.shouldForceReconnect(username, password) || con.isMaxAgeExpired();
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;
818 if (!forceReconnect) {
819 if ((!con.isDiscarded()) && con.validate(PooledConnection.VALIDATE_BORROW)) {
821 con.setTimestamp(now);
822 if (getPoolProperties().isLogAbandoned()) {
823 //set the stack trace for this pool
824 con.setStackTrace(getThreadDump());
826 if (!busy.offer(con)) {
827 log.debug("Connection doesn't fit into busy array, connection will not be traceable.");
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.
839 reconnectedCount.incrementAndGet();
840 int validationMode = getPoolProperties().isTestOnConnect() || getPoolProperties().getInitSQL()!=null ?
841 PooledConnection.VALIDATE_INIT :
842 PooledConnection.VALIDATE_BORROW;
844 if (con.validate(validationMode)) {
846 con.setTimestamp(now);
847 if (getPoolProperties().isLogAbandoned()) {
848 //set the stack trace for this pool
849 con.setStackTrace(getThreadDump());
851 if (!busy.offer(con)) {
852 log.debug("Connection doesn't fit into busy array, connection will not be traceable.");
857 throw new SQLException("Failed to validate a newly established connection.");
859 } catch (Exception x) {
862 if (x instanceof SQLException) {
863 throw (SQLException)x;
865 SQLException ex = new SQLException(x.getMessage());
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>
883 protected boolean terminateTransaction(PooledConnection con) {
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();
895 } catch (SQLException x) {
896 log.warn("Unable to terminate transaction, connection will be closed.",x);
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
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;
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
925 protected void returnConnection(PooledConnection con) {
927 //if the connection pool is closed
928 //close the connection instead of returning it
935 returnedCount.incrementAndGet();
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.");
943 jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.SUSPECT_RETURNED_NOTIFICATION,
944 "Connection(" + con + ") that has been marked suspect was returned.");
947 if (busy.remove(con)) {
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.");
959 if (log.isDebugEnabled()) {
960 log.debug("Connection ["+con+"] will be closed and not returned to the pool.");
965 if (log.isDebugEnabled()) {
966 log.debug("Connection ["+con+"] will be closed and not returned to the pool, busy.remove failed.");
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
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;
991 * Iterates through all the busy connections and checks for connections that have timed out
993 public void checkAbandoned() {
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;
1003 //the con has been returned to the pool or released
1005 if (idle.contains(con) || con.isReleased())
1007 long time = con.getTimestamp();
1008 long now = System.currentTimeMillis();
1009 if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) {
1013 } else if (sto > 0 && (now - time) > (sto * 1000L)) {
1024 } catch (ConcurrentModificationException e) {
1025 log.debug("checkAbandoned failed." ,e);
1026 } catch (Exception e) {
1027 log.warn("checkAbandoned failed, it will be retried.",e);
1032 * Iterates through the idle connections and resizes the idle pool based on parameters
1033 * {@link PoolProperties#maxIdle}, {@link PoolProperties#minIdle}, {@link PoolProperties#minEvictableIdleTimeMillis}
1035 public void checkIdle() {
1039 public void checkIdle(boolean ignoreMinSize) {
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;
1050 //the con been taken out, we can't clean it up
1051 if (busy.contains(con))
1053 long time = con.getTimestamp();
1054 if (shouldReleaseIdle(now, con, time)) {
1055 releasedIdleCount.incrementAndGet();
1068 } catch (ConcurrentModificationException e) {
1069 log.debug("checkIdle failed." ,e);
1070 } catch (Exception e) {
1071 log.warn("checkIdle failed, it will be retried.",e);
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());
1083 * Forces a validation of all idle connections if {@link PoolProperties#testWhileIdle} is set.
1085 public void testAllIdle() {
1087 if (idle.size()==0) return;
1088 Iterator<PooledConnection> unlocked = idle.iterator();
1089 while (unlocked.hasNext()) {
1090 PooledConnection con = unlocked.next();
1093 //the con been taken out, we can't clean it up
1094 if (busy.contains(con))
1096 if (!con.validate(PooledConnection.VALIDATE_IDLE)) {
1104 } catch (ConcurrentModificationException e) {
1105 log.debug("testAllIdle failed." ,e);
1106 } catch (Exception e) {
1107 log.warn("testAllIdle failed, it will be retried.",e);
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
1117 protected static String getThreadDump() {
1118 Exception x = new Exception();
1119 x.fillInStackTrace();
1120 return getStackTrace(x);
1124 * Convert an exception into a String
1125 * @param x - the throwable
1126 * @return a string representing the stack trace
1128 public static String getStackTrace(Throwable x) {
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;
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
1146 protected PooledConnection create(boolean incrementCounter) {
1147 if (incrementCounter) size.incrementAndGet();
1148 PooledConnection con = new PooledConnection(getPoolProperties(), this);
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()}
1159 public void purge() {
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()}
1169 public void purgeOnReturn() {
1170 poolVersion.incrementAndGet();
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
1177 protected void finalize(PooledConnection con) {
1178 JdbcInterceptor handler = con.getHandler();
1179 while (handler!=null) {
1180 handler.reset(null, null);
1181 handler=handler.getNext();
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
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();
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
1202 public org.apache.tomcat.jdbc.pool.jmx.ConnectionPool getJmxPool() {
1207 * Create MBean object that can be registered.
1209 protected void createMBean() {
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);
1218 * The total number of connections borrowed from this pool.
1219 * @return the borrowed connection count
1221 public long getBorrowedCount() {
1222 return borrowedCount.get();
1226 * The total number of connections returned to this pool.
1227 * @return the returned connection count
1229 public long getReturnedCount() {
1230 return returnedCount.get();
1234 * The total number of connections created by this pool.
1235 * @return the created connection count
1237 public long getCreatedCount() {
1238 return createdCount.get();
1242 * The total number of connections released from this pool.
1243 * @return the released connection count
1245 public long getReleasedCount() {
1246 return releasedCount.get();
1250 * The total number of connections reconnected by this pool.
1251 * @return the reconnected connection count
1253 public long getReconnectedCount() {
1254 return reconnectedCount.get();
1258 * The total number of connections released by remove abandoned.
1259 * @return the PoolCleaner removed abandoned connection count
1261 public long getRemoveAbandonedCount() {
1262 return removeAbandonedCount.get();
1266 * The total number of connections released by eviction.
1267 * @return the PoolCleaner evicted idle connection count
1269 public long getReleasedIdleCount() {
1270 return releasedIdleCount.get();
1274 * reset the statistics of this pool.
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);
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
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;
1306 public ConnectionFuture(PooledConnection pc) throws SQLException {
1308 result = ConnectionPool.this.setupConnection(pc);
1309 configured.set(true);
1315 public boolean cancel(boolean mayInterruptIfRunning) {
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);
1329 public Connection get() throws InterruptedException, ExecutionException {
1331 return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
1332 }catch (TimeoutException x) {
1333 throw new ExecutionException(x);
1341 public Connection get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
1342 PooledConnection pc = this.pc!=null?this.pc:pcFuture.get(timeout,unit);
1344 if (result!=null) return result;
1345 if (configured.compareAndSet(false, true)) {
1347 pc = borrowConnection(System.currentTimeMillis(),pc, null, null);
1348 result = ConnectionPool.this.setupConnection(pc);
1349 } catch (SQLException x) {
1355 //if we reach here, another thread is configuring the actual connection
1356 latch.await(timeout,unit); //this shouldn't block for long
1358 if (result==null) throw new ExecutionException(cause);
1369 public boolean isCancelled() {
1370 return pc==null && (pcFuture.isCancelled() || cancelled.get());
1377 public boolean isDone() {
1378 return pc!=null || pcFuture.isDone();
1382 * run method to be executed when cancelled by an executor
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);
1400 private static volatile Timer poolCleanTimer = null;
1401 private static HashSet<PoolCleaner> cleaners = new HashSet<>();
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();
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);
1416 Thread.currentThread().setContextClassLoader(loader);
1419 poolCleanTimer.schedule(cleaner, cleaner.sleepTime,cleaner.sleepTime);
1422 private static synchronized void unregisterCleaner(PoolCleaner cleaner) {
1423 boolean removed = cleaners.remove(cleaner);
1426 if (poolCleanTimer != null) {
1427 poolCleanTimer.purge();
1428 if (cleaners.size() == 0) {
1429 poolCleanTimer.cancel();
1430 poolCleanTimer = null;
1436 private static class PrivilegedNewTimer implements PrivilegedAction<Timer> {
1438 public Timer run() {
1439 return new Timer("Tomcat JDBC Pool Cleaner["+ System.identityHashCode(ConnectionPool.class.getClassLoader()) + ":"+
1440 System.currentTimeMillis() + "]", true);
1444 public static Set<TimerTask> getPoolCleaners() {
1445 return Collections.<TimerTask>unmodifiableSet(cleaners);
1448 public long getPoolVersion() {
1449 return poolVersion.get();
1452 public static Timer getPoolTimer() {
1453 return poolCleanTimer;
1456 protected static class PoolCleaner extends TimerTask {
1457 protected WeakReference<ConnectionPool> pool;
1458 protected long sleepTime;
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.");
1473 ConnectionPool pool = this.pool.get();
1476 } else if (!pool.isClosed()) {
1478 if (pool.getPoolProperties().isRemoveAbandoned()
1479 || pool.getPoolProperties().getSuspectTimeout() > 0)
1480 pool.checkAbandoned();
1481 if (pool.getPoolProperties().getMinIdle() < pool.idle
1484 if (pool.getPoolProperties().isTestWhileIdle())
1486 } catch (Exception x) {
1492 public void start() {
1493 registerCleaner(this);
1496 public void stopRunning() {
1497 unregisterCleaner(this);