From 062e998cf9c3e35c32396492641abf20ecc673cc Mon Sep 17 00:00:00 2001 From: fhanik Date: Thu, 23 Oct 2008 05:14:50 +0000 Subject: [PATCH] simple connection pool contribution - currently built using ant -f extras.xml conpool until we have a way to release it as a module, coming soon git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@707275 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 1 + extras.xml | 29 + .../apache/tomcat/jdbc/pool/ConnectionPool.java | 715 +++++++++++++++++++++ java/org/apache/tomcat/jdbc/pool/DataSource.java | 28 + .../apache/tomcat/jdbc/pool/DataSourceFactory.java | 427 ++++++++++++ .../apache/tomcat/jdbc/pool/DataSourceProxy.java | 304 +++++++++ java/org/apache/tomcat/jdbc/pool/Driver.java | 121 ++++ .../apache/tomcat/jdbc/pool/JdbcInterceptor.java | 51 ++ .../apache/tomcat/jdbc/pool/PoolProperties.java | 388 +++++++++++ .../apache/tomcat/jdbc/pool/PooledConnection.java | 294 +++++++++ .../apache/tomcat/jdbc/pool/ProxyConnection.java | 93 +++ .../jdbc/pool/interceptor/ConnectionState.java | 84 +++ .../jdbc/pool/interceptor/SlowQueryReport.java | 107 +++ .../tomcat/jdbc/pool/jmx/ConnectionPool.java | 171 +++++ .../tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java | 115 ++++ .../tomcat/jdbc/test/CheckOutThreadTest.java | 254 ++++++++ .../apache/tomcat/jdbc/test/DefaultProperties.java | 63 ++ .../apache/tomcat/jdbc/test/DefaultTestCase.java | 158 +++++ test/org/apache/tomcat/jdbc/test/TestGCClose.java | 35 + test/org/apache/tomcat/jdbc/test/TestTimeout.java | 92 +++ webapps/docs/config/jdbc-pool.xml | 298 +++++++++ 21 files changed, 3828 insertions(+) create mode 100644 java/org/apache/tomcat/jdbc/pool/ConnectionPool.java create mode 100644 java/org/apache/tomcat/jdbc/pool/DataSource.java create mode 100644 java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java create mode 100644 java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java create mode 100644 java/org/apache/tomcat/jdbc/pool/Driver.java create mode 100644 java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java create mode 100644 java/org/apache/tomcat/jdbc/pool/PoolProperties.java create mode 100644 java/org/apache/tomcat/jdbc/pool/PooledConnection.java create mode 100644 java/org/apache/tomcat/jdbc/pool/ProxyConnection.java create mode 100644 java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java create mode 100644 java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReport.java create mode 100644 java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java create mode 100644 java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPoolMBean.java create mode 100644 test/org/apache/tomcat/jdbc/test/CheckOutThreadTest.java create mode 100644 test/org/apache/tomcat/jdbc/test/DefaultProperties.java create mode 100644 test/org/apache/tomcat/jdbc/test/DefaultTestCase.java create mode 100644 test/org/apache/tomcat/jdbc/test/TestGCClose.java create mode 100644 test/org/apache/tomcat/jdbc/test/TestTimeout.java create mode 100644 webapps/docs/config/jdbc-pool.xml diff --git a/build.xml b/build.xml index 4eeb857d3..6a29726d4 100644 --- a/build.xml +++ b/build.xml @@ -121,6 +121,7 @@ + diff --git a/extras.xml b/extras.xml index 9483269ba..be34de17a 100644 --- a/extras.xml +++ b/extras.xml @@ -82,6 +82,8 @@ + + @@ -331,6 +333,33 @@ To run the sample application, copy the following applications into your CATALIN + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java new file mode 100644 index 000000000..1b594f32c --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java @@ -0,0 +1,715 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.management.ManagementFactory; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +import org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean; + +import java.util.concurrent.atomic.AtomicInteger; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; + +/** + * @author Filip Hanik + * @version 1.0 + */ + +public class ConnectionPool { + + //logger + protected static Log log = LogFactory.getLog(ConnectionPool.class); + + //=============================================================================== + // INSTANCE/QUICK ACCESS VARIABLE + //=============================================================================== + + /** + * All the information about the connection pool + */ + protected PoolProperties poolProperties; + + /** + * Contains all the connections that are in use + */ + protected BlockingQueue busy; + + /** + * Contains all the idle connections + */ + protected BlockingQueue idle; + + /** + * The thread that is responsible for checking abandoned and idle threads + */ + protected PoolCleaner poolCleaner; + + /** + * Pool closed flag + */ + protected boolean closed = false; + + /** + * Size of the pool + */ + protected AtomicInteger size = new AtomicInteger(0); + + /** + * Since newProxyInstance performs the same operation, over and over + * again, it is much more optimized if we simply store the constructor ourselves. + */ + protected Constructor proxyClassConstructor; + + + //=============================================================================== + // PUBLIC METHODS + //=============================================================================== + + /** + * Instantiate a connection pool. This will create connections if initialSize is larger than 0 + * @param prop PoolProperties - all the properties for this connection pool + * @throws SQLException + */ + public ConnectionPool(PoolProperties prop) throws SQLException { + //setup quick access variables and pools + init(prop); + } + + /** + * Borrows a connection from the pool + * @return Connection - a java.sql.Connection reflection proxy, wrapping the underlying object. + * @throws SQLException + */ + public Connection getConnection() throws SQLException { + //check out a connection + PooledConnection con = (PooledConnection)borrowConnection(); + JdbcInterceptor handler = con.getHandler(); + if (handler==null) { + //build the proxy handler + handler = new ProxyConnection(this,con); + //set up the interceptor chain + String[] proxies = getPoolProperties().getJdbcInterceptorsAsArray(); + for (int i=proxies.length-1; i>=0; i--) { + try { + JdbcInterceptor interceptor = + (JdbcInterceptor) Class.forName(proxies[i], true, + Thread.currentThread().getContextClassLoader()).newInstance(); + interceptor.setNext(handler); + handler = interceptor; + }catch(Exception x) { + SQLException sx = new SQLException("Unable to instantiate interceptor chain."); + sx.initCause(x); + throw sx; + } + } + //cache handler for the next iteration + con.setHandler(handler); + } else { + JdbcInterceptor next = handler; + //we have a cached handler, reset it + while (next!=null) { + next.reset(this, con); + next = next.getNext(); + } + } + + try { + //cache the constructor + if (proxyClassConstructor == null ) { + Class proxyClass = Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class}); + proxyClassConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class }); + } + //create the proxy + //TODO possible optimization, keep track if this connection was returned properly, and don't generate a new facade + Connection connection = (Connection)proxyClassConstructor.newInstance(new Object[] { handler }); + //return the connection + return connection; + }catch (Exception x) { + throw new SQLException(); + } + } + + /** + * Returns the name of this pool + * @return String + */ + public String getName() { + return getPoolProperties().getPoolName(); + } + + /** + * Returns the pool properties associated with this connection pool + * @return PoolProperties + */ + public PoolProperties getPoolProperties() { + return this.poolProperties; + } + + /** + * Returns the total size of this pool, this includes both busy and idle connections + * @return int + */ + public int getSize() { + return idle.size()+busy.size(); + } + + /** + * Returns the number of connections that are in use + * @return int + */ + public int getActive() { + return busy.size(); + } + + public int getIdle() { + return idle.size(); + } + + /** + * Returns true if {@link #close close} has been called, and the connection pool is unusable + * @return boolean + */ + public boolean isClosed() { + return this.closed; + } + + @Override + protected void finalize() throws Throwable { + close(true); + } + + /** + * Closes the pool and all disconnects all idle connections + * Active connections will be closed upon the {@link java.sql.Connection#close close} method is called + * on the underlying connection instead of being returned to the pool + * @param force - true to even close the active connections + */ + protected void close(boolean force) { + //are we already closed + if (this.closed) return; + //prevent other threads from entering + this.closed = true; + //stop background thread + if (poolCleaner!=null) { + poolCleaner.stopRunning(); + } + + /* release all idle connections */ + BlockingQueue pool = (idle.size()>0)?idle:(force?busy:idle); + while (pool.size()>0) { + try { + //retrieve the next connection + PooledConnection con = pool.poll(1000, TimeUnit.MILLISECONDS); + //close it and retrieve the next one, if one is available + while (con != null) { + //close the connection + if (pool==idle) + release(con); + else + abandon(con); + con = pool.poll(1000, TimeUnit.MILLISECONDS); + } //while + } catch (InterruptedException ex) { + Thread.currentThread().interrupted(); + } + if (pool.size()==0 && force && pool!=busy) pool = busy; + } + size.set(0); + if (this.getPoolProperties().isJmxEnabled()) stopJmx(); + } //closePool + + + //=============================================================================== + // PROTECTED METHODS + //=============================================================================== + /** + * Initialize the connection pool - called from the constructor + * @param properties PoolProperties - properties used to initialize the pool with + * @throws SQLException + */ + protected void init (PoolProperties properties) throws SQLException { + poolProperties = properties; + //make space for 10 extra in case we flow over a bit + busy = new ArrayBlockingQueue(properties.getMaxActive(),false); + //make space for 10 extra in case we flow over a bit + idle = new ArrayBlockingQueue(properties.getMaxActive(),false); + + //if the evictor thread is supposed to run, start it now + if (properties.isPoolSweeperEnabled()) { + poolCleaner = new PoolCleaner("[Pool-Cleaner]:" + properties.getName(), this, properties.getTimeBetweenEvictionRunsMillis()); + poolCleaner.start(); + } //end if + + if (properties.getMaxActive()properties.getMaxActive()) { + log.warn("minIdle is larger than maxActive, setting minIdle to: "+properties.getMaxActive()); + properties.setMinIdle(properties.getMaxActive()); + } + if (properties.getMaxIdle()>properties.getMaxActive()) { + log.warn("maxIdle is larger than maxActive, setting maxIdle to: "+properties.getMaxActive()); + properties.setMaxIdle(properties.getMaxActive()); + } + if (properties.getMaxIdle()= maxWait) { + throw new SQLException( + "Pool empty. Unable to fetch a connection in " + (maxWait / 1000) + + " seconds, none available["+busy.size()+" in use]."); + } else { + //no timeout, lets try again + continue; + } + } + } //while + } + + protected PooledConnection createConnection(long now, PooledConnection con) { + //no connections where available we'll create one + boolean error = false; + try { + //connect and validate the connection + con = create(); + con.lock(); + if (!busy.offer(con)) { + log.debug("Connection doesn't fit into busy array, connection will not be traceable."); + } + con.connect(); + if (con.validate(PooledConnection.VALIDATE_INIT)) { + //no need to lock a new one, its not contented + con.setTimestamp(now); + if (getPoolProperties().isLogAbandoned()) { + con.setStackTrace(getThreadDump()); + } + return con; + } //end if + } catch (Exception e) { + error = true; + log.error("Unable to create a new JDBC connection.", e); + } finally { + if (error ) { + release(con); + busy.remove(con); + } + con.unlock(); + }//catch + return null; + } + + protected PooledConnection borrowConnection(long now, PooledConnection con) { + //we have a connection, lets set it up + boolean setToNull = false; + try { + con.lock(); + if (con.isDiscarded()) { + //connection has already been disconnected + setToNull = true; + } else if (con.validate(PooledConnection.VALIDATE_BORROW)) { + //set the timestamp + con.setTimestamp(now); + if (getPoolProperties().isLogAbandoned()) { + //set the stack trace for this pool + con.setStackTrace(getThreadDump()); + } + if (!busy.offer(con)) { + log.debug("Connection doesn't fit into busy array, connection will not be traceable."); + } + return con; + } else { + /*if the object wasn't validated, we may as well remove it*/ + release(con); + setToNull = true; + } //end if + } finally { + con.unlock(); + if (setToNull) { + con = null; + } + } + return con; + } + + /** + * Returns a connection to the pool + * @param con PooledConnection + */ + protected void returnConnection(PooledConnection con) { + if (isClosed()) { + //if the connection pool is closed + //close the connection instead of returning it + release(con); + return; + } //end if + + if (con != null) { + try { + con.lock(); + + if (busy.remove(con)) { + if ((!con.isDiscarded()) && (!isClosed()) && + con.validate(PooledConnection.VALIDATE_RETURN)) { + con.setStackTrace(null); + con.setTimestamp(System.currentTimeMillis()); + if (!idle.offer(con)) { + if (log.isDebugEnabled()) { + log.debug("Connection ["+con+"] will be closed and not returned to the pool, idle.offer failed."); + } + release(con); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Connection ["+con+"] will be closed and not returned to the pool."); + } + release(con); + } //end if + } else { + if (log.isDebugEnabled()) { + log.debug("Connection ["+con+"] will be closed and not returned to the pool, busy.remove failed."); + } + release(con); + } + } finally { + con.unlock(); + } + } //end if + } //checkIn + + public void checkAbandoned() { + try { + long now = System.currentTimeMillis(); + Iterator locked = busy.iterator(); + while (locked.hasNext()) { + PooledConnection con = locked.next(); + boolean setToNull = false; + try { + con.lock(); + //the con has been returned to the pool + //ignore it + if (idle.contains(con)) + continue; + long time = con.getTimestamp(); + if ((now - time) > con.getAbandonTimeout()) { + busy.remove(con); + abandon(con); + release(con); + setToNull = true; + } else { + //do nothing + } //end if + } finally { + con.unlock(); + if (setToNull) + con = null; + } + } //while + } catch (ConcurrentModificationException e) { + log.debug("checkAbandoned failed." ,e); + } catch (Exception e) { + log.warn("checkAbandoned failed, it will be retried.",e); + } + } + + public void checkIdle() { + try { + long now = System.currentTimeMillis(); + Iterator unlocked = idle.iterator(); + while ( (idle.size()>=getPoolProperties().getMinIdle()) && unlocked.hasNext()) { + PooledConnection con = unlocked.next(); + boolean setToNull = false; + try { + con.lock(); + //the con been taken out, we can't clean it up + if (busy.contains(con)) + continue; + long time = con.getTimestamp(); + if (((now - time) > con.getReleaseTime()) && (getSize()>getPoolProperties().getMinIdle())) { + release(con); + idle.remove(con); + setToNull = true; + } else { + //do nothing + } //end if + } finally { + con.unlock(); + if (setToNull) + con = null; + } + } //while + } catch (ConcurrentModificationException e) { + log.debug("checkIdle failed." ,e); + } catch (Exception e) { + log.warn("checkIdle failed, it will be retried.",e); + } + + } + + public void testAllIdle() { + try { + Iterator unlocked = idle.iterator(); + while (unlocked.hasNext()) { + PooledConnection con = unlocked.next(); + try { + con.lock(); + //the con been taken out, we can't clean it up + if (busy.contains(con)) + continue; + if (!con.validate(PooledConnection.VALIDATE_IDLE)) { + idle.remove(con); + con.release(); + } + } finally { + con.unlock(); + } + } //while + } catch (ConcurrentModificationException e) { + log.debug("testAllIdle failed." ,e); + } catch (Exception e) { + log.warn("testAllIdle failed, it will be retried.",e); + } + + } + + + protected static String getThreadDump() { + Exception x = new Exception(); + x.fillInStackTrace(); + return getStackTrace(x); + } + + protected static String getStackTrace(Exception x) { + if (x == null) { + return null; + } else { + java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream(); + java.io.PrintStream writer = new java.io.PrintStream(bout); + x.printStackTrace(writer); + String result = bout.toString(); + return result; + } //end if + } + + + protected PooledConnection create() throws java.lang.Exception { + PooledConnection con = new PooledConnection(getPoolProperties(), this); + return con; + } + + protected void finalize(PooledConnection con) { + size.addAndGet(-1); + } + + public void startJmx() { + try { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName name = new ObjectName("org.apache.tomcat.jdbc.pool.jmx:type=ConnectionPool,name="+getName()); + mbs.registerMBean(new org.apache.tomcat.jdbc.pool.jmx.ConnectionPool(this), name); + } catch (Exception x) { + log.warn("Unable to start JMX integration for connection pool. Instance["+getName()+"] can't be monitored.",x); + } + } + + public void stopJmx() { + try { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + ObjectName name = new ObjectName("org.apache.tomcat.jdbc.pool.jmx:type=ConnectionPool,name="+getName()); + mbs.unregisterMBean(name); + }catch (Exception x) { + log.warn("Unable to stop JMX integration for connection pool. Instance["+getName()+"].",x); + } + } + + + protected class PoolCleaner extends Thread { + protected ConnectionPool pool; + protected long sleepTime; + protected boolean run = true; + PoolCleaner(String name, ConnectionPool pool, long sleepTime) { + super(name); + this.setDaemon(true); + this.pool = pool; + this.sleepTime = sleepTime; + if (sleepTime <= 0) { + pool.log.warn("Database connection pool evicter thread interval is set to 0, defaulting to 30 seconds"); + this.sleepTime = 1000 * 30; + } else if (sleepTime < 1000) { + pool.log.warn("Database connection pool evicter thread interval is set to lower than 1 second."); + } + } + + public void run() { + while (run) { + try { + sleep(sleepTime); + } catch (InterruptedException e) { + // ignore it + Thread.currentThread().interrupted(); + continue; + } //catch + + if (pool.isClosed()) { + if (pool.getSize() <= 0) { + run = false; + } + } else { + try { + if (pool.getPoolProperties().isRemoveAbandoned()) + pool.checkAbandoned(); + if (pool.getPoolProperties().getMaxIdle()JNDI object factory that creates an instance of + * BasicDataSource that has been configured based on the + * RefAddr values of the specified Reference, + * which must match the names and data types of the + * BasicDataSource bean properties.

+ *
+ * Properties available for configuration:
+ * Commons DBCP properties
+ *
    + *
  1. initSQL - A query that gets executed once, right after the connection is established.
  2. + *
  3. testOnConnect - run validationQuery after connection has been established.
  4. + *
  5. validationInterval - avoid excess validation, only run validation at most at this frequency - time in milliseconds.
  6. + *
  7. jdbcInterceptors - a semicolon separated list of classnames extending {@link JdbcInterceptor} class.
  8. + *
  9. jmxEnabled - true of false, whether to register the pool with JMX.
  10. + *
+ * @author Craig R. McClanahan + * @author Dirk Verbeeck + * @author Filip Hanik + */ +public class DataSourceFactory implements ObjectFactory { + protected static Log log = LogFactory.getLog(DataSourceFactory.class); + + protected final static String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; + protected final static String PROP_DEFAULTREADONLY = "defaultReadOnly"; + protected final static String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; + protected final static String PROP_DEFAULTCATALOG = "defaultCatalog"; + + protected final static String PROP_DRIVERCLASSNAME = "driverClassName"; + protected final static String PROP_PASSWORD = "password"; + protected final static String PROP_URL = "url"; + protected final static String PROP_USERNAME = "username"; + + protected final static String PROP_MAXACTIVE = "maxActive"; + protected final static String PROP_MAXIDLE = "maxIdle"; + protected final static String PROP_MINIDLE = "minIdle"; + protected final static String PROP_INITIALSIZE = "initialSize"; + protected final static String PROP_MAXWAIT = "maxWait"; + + protected final static String PROP_TESTONBORROW = "testOnBorrow"; + protected final static String PROP_TESTONRETURN = "testOnReturn"; + protected final static String PROP_TESTWHILEIDLE = "testWhileIdle"; + protected final static String PROP_TESTONCONNECT = "testOnConnect"; + protected final static String PROP_VALIDATIONQUERY = "validationQuery"; + + protected final static String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; + protected final static String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; + protected final static String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; + + protected final static String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; + + protected final static String PROP_REMOVEABANDONED = "removeAbandoned"; + protected final static String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; + protected final static String PROP_LOGABANDONED = "logAbandoned"; + + protected final static String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; + protected final static String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; + protected final static String PROP_CONNECTIONPROPERTIES = "connectionProperties"; + + protected final static String PROP_INITSQL = "initSQL"; + protected final static String PROP_INTERCEPTORS = "jdbcInterceptors"; + protected final static String PROP_VALIDATIONINTERVAL = "validationInterval"; + protected final static String PROP_JMX_ENABLED = "jmxEnabled"; + + public static final int UNKNOWN_TRANSACTIONISOLATION = -1; + + + protected final static String[] ALL_PROPERTIES = { + PROP_DEFAULTAUTOCOMMIT, + PROP_DEFAULTREADONLY, + PROP_DEFAULTTRANSACTIONISOLATION, + PROP_DEFAULTCATALOG, + PROP_DRIVERCLASSNAME, + PROP_MAXACTIVE, + PROP_MAXIDLE, + PROP_MINIDLE, + PROP_INITIALSIZE, + PROP_MAXWAIT, + PROP_TESTONBORROW, + PROP_TESTONRETURN, + PROP_TIMEBETWEENEVICTIONRUNSMILLIS, + PROP_NUMTESTSPEREVICTIONRUN, + PROP_MINEVICTABLEIDLETIMEMILLIS, + PROP_TESTWHILEIDLE, + PROP_TESTONCONNECT, + PROP_PASSWORD, + PROP_URL, + PROP_USERNAME, + PROP_VALIDATIONQUERY, + PROP_VALIDATIONINTERVAL, + PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, + PROP_REMOVEABANDONED, + PROP_REMOVEABANDONEDTIMEOUT, + PROP_LOGABANDONED, + PROP_POOLPREPAREDSTATEMENTS, + PROP_MAXOPENPREPAREDSTATEMENTS, + PROP_CONNECTIONPROPERTIES, + PROP_INITSQL, + PROP_INTERCEPTORS, + PROP_JMX_ENABLED + }; + + // -------------------------------------------------- ObjectFactory Methods + + /** + *

Create and return a new BasicDataSource instance. If no + * instance can be created, return null instead.

+ * + * @param obj The possibly null object containing location or + * reference information that can be used in creating an object + * @param name The name of this object relative to nameCtx + * @param nameCtx The context relative to which the name + * parameter is specified, or null if name + * is relative to the default initial context + * @param environment The possibly null environment that is used in + * creating this object + * + * @exception Exception if an exception occurs creating the instance + */ + public Object getObjectInstance(Object obj, Name name, Context nameCtx, + Hashtable environment) throws Exception { + + // We only know how to deal with javax.naming.References + // that specify a class name of "javax.sql.DataSource" + if ((obj == null) || !(obj instanceof Reference)) { + return null; + } + Reference ref = (Reference) obj; + if (!"javax.sql.DataSource".equals(ref.getClassName())) { + return null; + } + + Properties properties = new Properties(); + for (int i = 0; i < ALL_PROPERTIES.length; i++) { + String propertyName = ALL_PROPERTIES[i]; + RefAddr ra = ref.get(propertyName); + if (ra != null) { + String propertyValue = ra.getContent().toString(); + properties.setProperty(propertyName, propertyValue); + } + } + + return createDataSource(properties); + } + + /** + * Creates and configures a {@link BasicDataSource} instance based on the + * given properties. + * + * @param properties the datasource configuration properties + * @throws Exception if an error occurs creating the data source + */ + public static DataSource createDataSource(Properties properties) throws Exception { + org.apache.tomcat.jdbc.pool.DataSourceProxy dataSource = new org.apache.tomcat.jdbc.pool.DataSourceProxy(); + + String value = null; + + value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); + if (value != null) { + dataSource.getPoolProperties().setDefaultAutoCommit(Boolean.valueOf(value)); + } + + value = properties.getProperty(PROP_DEFAULTREADONLY); + if (value != null) { + dataSource.getPoolProperties().setDefaultReadOnly(Boolean.valueOf(value)); + } + + value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); + if (value != null) { + int level = UNKNOWN_TRANSACTIONISOLATION; + if ("NONE".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_NONE; + } else if ("READ_COMMITTED".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_READ_COMMITTED; + } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_READ_UNCOMMITTED; + } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_REPEATABLE_READ; + } else if ("SERIALIZABLE".equalsIgnoreCase(value)) { + level = Connection.TRANSACTION_SERIALIZABLE; + } else { + try { + level = Integer.parseInt(value); + } catch (NumberFormatException e) { + System.err.println("Could not parse defaultTransactionIsolation: " + value); + System.err.println("WARNING: defaultTransactionIsolation not set"); + System.err.println("using default value of database driver"); + level = UNKNOWN_TRANSACTIONISOLATION; + } + } + dataSource.getPoolProperties().setDefaultTransactionIsolation(level); + } + + value = properties.getProperty(PROP_DEFAULTCATALOG); + if (value != null) { + dataSource.getPoolProperties().setDefaultCatalog(value); + } + + value = properties.getProperty(PROP_DRIVERCLASSNAME); + if (value != null) { + dataSource.getPoolProperties().setDriverClassName(value); + } + + value = properties.getProperty(PROP_MAXACTIVE); + if (value != null) { + dataSource.getPoolProperties().setMaxActive(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXIDLE); + if (value != null) { + dataSource.getPoolProperties().setMaxIdle(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MINIDLE); + if (value != null) { + dataSource.getPoolProperties().setMinIdle(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_INITIALSIZE); + if (value != null) { + dataSource.getPoolProperties().setInitialSize(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MAXWAIT); + if (value != null) { + dataSource.getPoolProperties().setMaxWait(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_TESTONBORROW); + if (value != null) { + dataSource.getPoolProperties().setTestOnBorrow(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_TESTONRETURN); + if (value != null) { + dataSource.getPoolProperties().setTestOnReturn(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_TESTONCONNECT); + if (value != null) { + dataSource.getPoolProperties().setTestOnConnect(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); + if (value != null) { + dataSource.getPoolProperties().setTimeBetweenEvictionRunsMillis(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); + if (value != null) { + dataSource.getPoolProperties().setNumTestsPerEvictionRun(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); + if (value != null) { + dataSource.getPoolProperties().setMinEvictableIdleTimeMillis(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_TESTWHILEIDLE); + if (value != null) { + dataSource.getPoolProperties().setTestWhileIdle(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_PASSWORD); + if (value != null) { + dataSource.getPoolProperties().setPassword(value); + } + + value = properties.getProperty(PROP_URL); + if (value != null) { + dataSource.getPoolProperties().setUrl(value); + } + + value = properties.getProperty(PROP_USERNAME); + if (value != null) { + dataSource.getPoolProperties().setUsername(value); + } + + value = properties.getProperty(PROP_VALIDATIONQUERY); + if (value != null) { + dataSource.getPoolProperties().setValidationQuery(value); + } + + value = properties.getProperty(PROP_VALIDATIONINTERVAL); + if (value != null) { + dataSource.getPoolProperties().setValidationInterval(Long.parseLong(value)); + } + + value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); + if (value != null) { + dataSource.getPoolProperties(). + setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_REMOVEABANDONED); + if (value != null) { + dataSource.getPoolProperties().setRemoveAbandoned(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); + if (value != null) { + dataSource.getPoolProperties().setRemoveAbandonedTimeout(Integer.parseInt(value)); + } + + value = properties.getProperty(PROP_LOGABANDONED); + if (value != null) { + dataSource.getPoolProperties().setLogAbandoned(Boolean.valueOf(value).booleanValue()); + } + + value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); + if (value != null) { + log.warn(PROP_POOLPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect."); + } + + value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); + if (value != null) { + log.warn(PROP_MAXOPENPREPAREDSTATEMENTS + " is not a valid setting, it will have no effect."); + } + + value = properties.getProperty(PROP_CONNECTIONPROPERTIES); + if (value != null) { + Properties p = getProperties(value); + dataSource.getPoolProperties().setDbProperties(p); + } else { + dataSource.getPoolProperties().setDbProperties(new Properties()); + } + + dataSource.getPoolProperties().getDbProperties().setProperty("user",dataSource.getPoolProperties().getUsername()); + dataSource.getPoolProperties().getDbProperties().setProperty("password",dataSource.getPoolProperties().getPassword()); + + value = properties.getProperty(PROP_INITSQL); + if (value != null) { + dataSource.getPoolProperties().setInitSQL(value); + } + + value = properties.getProperty(PROP_INTERCEPTORS); + if (value != null) { + dataSource.getPoolProperties().setJdbcInterceptors(value); + } + + value = properties.getProperty(PROP_JMX_ENABLED); + if (value != null) { + dataSource.getPoolProperties().setJmxEnabled(Boolean.parseBoolean(value)); + } + + // Return the configured DataSource instance + DataSource ds = getDataSource(dataSource); + return ds; + } + + public static DataSource getDataSource(org.apache.tomcat.jdbc.pool.DataSourceProxy dataSource) { + DataSourceHandler handler = new DataSourceHandler(dataSource); + DataSource ds = (DataSource)Proxy.newProxyInstance(DataSourceFactory.class.getClassLoader(), new Class[] {javax.sql.DataSource.class}, handler); + return ds; + } + + /** + *

Parse properties from the string. Format of the string must be [propertyName=property;]*

+ * @param propText + * @return Properties + * @throws Exception + */ + static protected Properties getProperties(String propText) throws Exception { + Properties p = new Properties(); + if (propText != null) { + p.load(new ByteArrayInputStream(propText.replace(';', '\n'). + getBytes())); + } + return p; + } + + protected static class DataSourceHandler implements InvocationHandler { + protected org.apache.tomcat.jdbc.pool.DataSourceProxy datasource = null; + protected static HashMap methods = new HashMap(); + public DataSourceHandler(org.apache.tomcat.jdbc.pool.DataSourceProxy ds) { + this.datasource = ds; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method m = methods.get(method); + if (m==null) { + m = datasource.getClass().getMethod(method.getName(), method.getParameterTypes()); + methods.put(method, m); + } + return m.invoke(datasource, args); + } + + } +} diff --git a/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java b/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java new file mode 100644 index 000000000..878585c2f --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Iterator; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + * + *

Title: Uber Pool

+ * + *

Description: A simple, yet efficient and powerful connection pool

+ * + *

Copyright: Copyright (c) 2008 Filip Hanik

+ * + *

+ * + * @author Filip Hanik + * @version 1.0 + */ + +public class DataSourceProxy { + protected static Log log = LogFactory.getLog(DataSourceProxy.class); + + protected Driver driver; + protected PoolProperties poolProperties = new PoolProperties(); + + public DataSourceProxy() { + } + + + public boolean isWrapperFor(Class iface) throws SQLException { + // we are not a wrapper of anything + return false; + } + + + public T unwrap(Class iface) throws SQLException { + //we can't unwrap anything + return null; + } + + /** + * {@inheritDoc} + */ + public Connection getConnection(String username, String password) throws SQLException { + return getConnection(); + } + + public PoolProperties getPoolProperties() { + return poolProperties; + } + + /** + * Sets up the connection pool, by creating a pooling driver. + * @return Driver + * @throws SQLException + */ + public synchronized Driver createDriver() throws SQLException { + if (driver != null) { + return driver; + } else { + driver = new org.apache.tomcat.jdbc.pool.Driver(getPoolProperties()); + return driver; + } + } + + /** + * {@inheritDoc} + */ + + public Connection getConnection() throws SQLException { + if (driver == null) + driver = createDriver(); + return driver.connect(poolProperties.getPoolName(), null); + } + + /** + * {@inheritDoc} + */ + public PooledConnection getPooledConnection() throws SQLException { + return (PooledConnection) getConnection(); + } + + /** + * {@inheritDoc} + */ + public PooledConnection getPooledConnection(String username, + String password) throws SQLException { + return (PooledConnection) getConnection(); + } + + /** + * {@inheritDoc} + */ + public PrintWriter getLogWriter() throws SQLException { + return null; + } + + /** + * {@inheritDoc} + */ + public void setLogWriter(PrintWriter out) throws SQLException { + } + + /** + * {@inheritDoc} + */ + public int getLoginTimeout() { + if (poolProperties == null) { + return 0; + } else { + return poolProperties.getMaxWait() / 1000; + } + } + + /** + * {@inheritDoc} + */ + public void setLoginTimeout(int i) { + if (poolProperties == null) { + return; + } else { + poolProperties.setMaxWait(1000 * i); + } + + } + + + public void close() { + close(false); + } + public void close(boolean all) { + try { + if (driver != null) { + Driver d = driver; + driver = null; + d.closePool(poolProperties.getPoolName(), all); + } + }catch (Exception x) { + x.printStackTrace(); + } + } + + protected void finalize() throws Throwable { + //terminate the pool? + close(true); + } + + public int getPoolSize() throws SQLException{ + if (driver == null) + driver = createDriver(); + return driver.getPool(getPoolProperties().getPoolName()).getSize(); + } + + public String toString() { + return super.toString()+"{"+getPoolProperties()+"}"; + } + +/*-----------------------------------------------------------------------*/ +// PROPERTIES WHEN NOT USED WITH FACTORY +/*------------------------------------------------------------------------*/ + public void setPoolProperties(PoolProperties poolProperties) { + this.poolProperties = poolProperties; + } + + public void setDriverClassName(String driverClassName) { + this.poolProperties.setDriverClassName(driverClassName); + } + + public void setInitialSize(int initialSize) { + this.poolProperties.setInitialSize(initialSize); + } + + public void setInitSQL(String initSQL) { + this.poolProperties.setInitSQL(initSQL); + } + + public void setLogAbandoned(boolean logAbandoned) { + this.poolProperties.setLogAbandoned(logAbandoned); + } + + public void setMaxActive(int maxActive) { + this.poolProperties.setMaxIdle(maxActive); + } + + public void setMaxIdle(int maxIdle) { + this.poolProperties.setMaxIdle(maxIdle); + } + + public void setMaxWait(int maxWait) { + this.poolProperties.setMaxWait(maxWait); + } + + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + this.poolProperties.setMinEvictableIdleTimeMillis( + minEvictableIdleTimeMillis); + } + + public void setMinIdle(int minIdle) { + this.setMinIdle(minIdle); + } + + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + this.poolProperties.setNumTestsPerEvictionRun(numTestsPerEvictionRun); + } + + public void setPassword(String password) { + this.poolProperties.setPassword(password); + this.poolProperties.getDbProperties().setProperty("password",this.poolProperties.getPassword()); + } + + public void setRemoveAbandoned(boolean removeAbandoned) { + this.poolProperties.setRemoveAbandoned(removeAbandoned); + } + + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) { + this.poolProperties.setRemoveAbandonedTimeout(removeAbandonedTimeout); + } + + public void setTestOnBorrow(boolean testOnBorrow) { + this.poolProperties.setTestOnBorrow(testOnBorrow); + } + + public void setTestOnConnect(boolean testOnConnect) { + this.poolProperties.setTestOnConnect(testOnConnect); + } + + public void setTestOnReturn(boolean testOnReturn) { + this.poolProperties.setTestOnReturn(testOnReturn); + } + + public void setTestWhileIdle(boolean testWhileIdle) { + this.poolProperties.setTestWhileIdle(testWhileIdle); + } + + public void setTimeBetweenEvictionRunsMillis(int + timeBetweenEvictionRunsMillis) { + this.poolProperties.setTimeBetweenEvictionRunsMillis( + timeBetweenEvictionRunsMillis); + } + + public void setUrl(String url) { + this.poolProperties.setUrl(url); + } + + public void setUsername(String username) { + this.poolProperties.setUsername(username); + this.poolProperties.getDbProperties().setProperty("user",getPoolProperties().getUsername()); + } + + public void setValidationInterval(long validationInterval) { + this.poolProperties.setValidationInterval(validationInterval); + } + + public void setValidationQuery(String validationQuery) { + this.poolProperties.setValidationQuery(validationQuery); + } + + public void setJdbcInterceptors(String interceptors) { + this.getPoolProperties().setJdbcInterceptors(interceptors); + } + + public void setJmxEnabled(boolean enabled) { + this.getPoolProperties().setJmxEnabled(enabled); + } + + public void setConnectionProperties(String properties) { + try { + java.util.Properties prop = DataSourceFactory.getProperties(properties); + Iterator i = prop.keySet().iterator(); + while (i.hasNext()) { + String key = (String)i.next(); + String value = prop.getProperty(key); + getPoolProperties().getDbProperties().setProperty(key, value); + } + + }catch (Exception x) { + log.error("Unable to parse connection properties.", x); + throw new RuntimeException(x); + } + } + + +} diff --git a/java/org/apache/tomcat/jdbc/pool/Driver.java b/java/org/apache/tomcat/jdbc/pool/Driver.java new file mode 100644 index 000000000..9e03825ba --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/Driver.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + + +import java.sql.Connection; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Properties; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +/** + * @author Filip Hanik + * @version 1.0 + */ +public class Driver implements java.sql.Driver { + + protected static Log log = LogFactory.getLog(Driver.class); + + protected static HashMap pooltable = new HashMap(11); + + public Driver() throws SQLException { + } + + public Driver(PoolProperties properties) throws SQLException { + init(properties); + } //Driver + + public void init(PoolProperties properties) throws SQLException { + if (pooltable.get(properties.getPoolName()) != null) + throw new SQLException("Pool identified by:" + properties.getPoolName() + " already exists."); + ConnectionPool pool = new ConnectionPool(properties); + pooltable.put(properties.getPoolName(), pool); + } + + public void closePool(String url, boolean all) throws SQLException { + ConnectionPool pool = (ConnectionPool) pooltable.get(url); + if (pool == null) { + throw new SQLException("No connection pool established for URL:" + url); + } else { + pool.close(all); + } + pooltable.remove(url); + } + + /** + * {@inheritDoc} + */ + public Connection connect(String url, Properties info) throws SQLException { + ConnectionPool pool = (ConnectionPool) pooltable.get(url); + if (pool == null) { + throw new SQLException("No connection pool established for URL:" + url); + } else { + try { + return pool.getConnection(); + } catch (SQLException forward) { + throw forward; + } catch (Exception e) { + throw new SQLException("Unknow pool exception:" + ConnectionPool.getStackTrace(e)); + } //catch + } //end if + } //connect + + /** + * {@inheritDoc} + */ + public boolean acceptsURL(String url) throws SQLException { + /* check if the driver has a connection pool with that name */ + return (pooltable.get(url) != null ? true : false); + } //acceptsUrl + + /** + * {@inheritDoc} + */ + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws + SQLException { + return new DriverPropertyInfo[0]; + } //getPropertyInfo + + /** + * {@inheritDoc} + */ + public int getMajorVersion() { + return 1; + } + + /** + * {@inheritDoc} + */ + public int getMinorVersion() { + return 0; + } + + /** + * {@inheritDoc} + */ + public boolean jdbcCompliant() { + return true; + } + + public ConnectionPool getPool(String url) throws SQLException { + return (ConnectionPool) pooltable.get(url); + } + +} //class diff --git a/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java b/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java new file mode 100644 index 000000000..67d1086b2 --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +/** + * @author Filip Hanik + * @version 1.0 + */ +public abstract class JdbcInterceptor implements InvocationHandler { + public static final String CLOSE_VAL = "close"; + + private JdbcInterceptor next = null; + + public JdbcInterceptor() { + } + + /** + * {@inheritDoc} + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (getNext()!=null) return getNext().invoke(this,method,args); + else throw new NullPointerException(); + } + + public JdbcInterceptor getNext() { + return next; + } + + public void setNext(JdbcInterceptor next) { + this.next = next; + } + + public abstract void reset(ConnectionPool parent, PooledConnection con); +} diff --git a/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/java/org/apache/tomcat/jdbc/pool/PoolProperties.java new file mode 100644 index 000000000..6f94d3694 --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/PoolProperties.java @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + + +import java.lang.reflect.Method; +import java.util.Properties; +/** + * @author Filip Hanik + * + */ +public class PoolProperties { + protected static volatile int poolCounter = 1; + protected Properties dbProperties = new Properties(); + protected String url = null; + protected String driverClassName = null; + protected Boolean defaultAutoCommit = null; + protected Boolean defaultReadOnly = null; + protected int defaultTransactionIsolation = DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION; + protected String defaultCatalog = null; + protected String connectionProperties; + protected int initialSize = 10; + protected int maxActive = 100; + protected int maxIdle = maxActive; + protected int minIdle = initialSize; + protected int maxWait = 30000; + protected String validationQuery; + protected boolean testOnBorrow = false; + protected boolean testOnReturn = false; + protected boolean testWhileIdle = false; + protected int timeBetweenEvictionRunsMillis = 5000; + protected int numTestsPerEvictionRun; + protected int minEvictableIdleTimeMillis = 60000; + protected boolean accessToUnderlyingConnectionAllowed; + protected boolean removeAbandoned = false; + protected int removeAbandonedTimeout = 60; + protected boolean logAbandoned = false; + protected int loginTimeout = 10000; + protected String name = "Filip Connection Pool["+(poolCounter++)+"]"; + protected String password; + protected String username; + protected long validationInterval = 30000; + protected boolean jmxEnabled = true; + protected String initSQL; + protected boolean testOnConnect =false; + private String jdbcInterceptors=null; + + public boolean isAccessToUnderlyingConnectionAllowed() { + return accessToUnderlyingConnectionAllowed; + } + + public String getConnectionProperties() { + return connectionProperties; + } + + public Properties getDbProperties() { + return dbProperties; + } + + public boolean isDefaultAutoCommit() { + return defaultAutoCommit; + } + + public String getDefaultCatalog() { + return defaultCatalog; + } + + public boolean isDefaultReadOnly() { + return defaultReadOnly; + } + + public int getDefaultTransactionIsolation() { + return defaultTransactionIsolation; + } + + public String getDriverClassName() { + return driverClassName; + } + + public int getInitialSize() { + return initialSize; + } + + public boolean isLogAbandoned() { + return logAbandoned; + } + + public int getLoginTimeout() { + return loginTimeout; + } + + public int getMaxActive() { + return maxActive; + } + + public int getMaxIdle() { + return maxIdle; + } + + public int getMaxWait() { + return maxWait; + } + + public int getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + public int getMinIdle() { + return minIdle; + } + + public String getName() { + return name; + } + + public int getNumTestsPerEvictionRun() { + return numTestsPerEvictionRun; + } + + public String getPassword() { + return password; + } + + public String getPoolName() { + return getName(); + } + + public boolean isRemoveAbandoned() { + return removeAbandoned; + } + + public int getRemoveAbandonedTimeout() { + return removeAbandonedTimeout; + } + + public boolean isTestOnBorrow() { + return testOnBorrow; + } + + public boolean isTestOnReturn() { + return testOnReturn; + } + + public boolean isTestWhileIdle() { + return testWhileIdle; + } + + public int getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getValidationQuery() { + return validationQuery; + } + + public long getValidationInterval() { + return validationInterval; + } + + public String getInitSQL() { + return initSQL; + } + + public boolean isTestOnConnect() { + return testOnConnect; + } + + public String getJdbcInterceptors() { + return jdbcInterceptors; + } + + public String[] getJdbcInterceptorsAsArray() { + if (jdbcInterceptors==null) return new String[0]; + else { + return jdbcInterceptors.split(";"); + } + } + + public void setAccessToUnderlyingConnectionAllowed(boolean + accessToUnderlyingConnectionAllowed) { + this.accessToUnderlyingConnectionAllowed = + accessToUnderlyingConnectionAllowed; + } + + public void setConnectionProperties(String connectionProperties) { + this.connectionProperties = connectionProperties; + } + + public void setDbProperties(Properties dbProperties) { + this.dbProperties = dbProperties; + } + + public void setDefaultAutoCommit(Boolean defaultAutoCommit) { + this.defaultAutoCommit = defaultAutoCommit; + } + + public void setDefaultCatalog(String defaultCatalog) { + this.defaultCatalog = defaultCatalog; + } + + public void setDefaultReadOnly(Boolean defaultReadOnly) { + this.defaultReadOnly = defaultReadOnly; + } + + public void setDefaultTransactionIsolation(int defaultTransactionIsolation) { + this.defaultTransactionIsolation = defaultTransactionIsolation; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + public void setInitialSize(int initialSize) { + this.initialSize = initialSize; + } + + public void setLogAbandoned(boolean logAbandoned) { + this.logAbandoned = logAbandoned; + } + + public void setLoginTimeout(int loginTimeout) { + this.loginTimeout = loginTimeout; + } + + public void setMaxActive(int maxActive) { + this.maxActive = maxActive; + } + + public void setMaxIdle(int maxIdle) { + this.maxIdle = maxIdle; + } + + public void setMaxWait(int maxWait) { + this.maxWait = maxWait; + } + + public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + public void setMinIdle(int minIdle) { + this.minIdle = minIdle; + } + + public void setName(String name) { + this.name = name; + } + + public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { + this.numTestsPerEvictionRun = numTestsPerEvictionRun; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setRemoveAbandoned(boolean removeAbandoned) { + this.removeAbandoned = removeAbandoned; + } + + public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) { + this.removeAbandonedTimeout = removeAbandonedTimeout; + } + + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + public void setTestWhileIdle(boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + + public void setTimeBetweenEvictionRunsMillis(int + timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setValidationInterval(long validationInterval) { + this.validationInterval = validationInterval; + } + + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + public void setInitSQL(String initSQL) { + this.initSQL = initSQL; + } + + public void setTestOnConnect(boolean testOnConnect) { + this.testOnConnect = testOnConnect; + } + + public void setJdbcInterceptors(String jdbcInterceptors) { + this.jdbcInterceptors = jdbcInterceptors; + } + + public String toString() { + StringBuffer buf = new StringBuffer("ConnectionPool["); + try { + String[] fields = DataSourceFactory.ALL_PROPERTIES; + for (int i=0; i0; + result = result && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0); + result = result && (isTestWhileIdle() && getValidationQuery()!=null); + return result; + } +} diff --git a/java/org/apache/tomcat/jdbc/pool/PooledConnection.java b/java/org/apache/tomcat/jdbc/pool/PooledConnection.java new file mode 100644 index 000000000..79983912a --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/PooledConnection.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + + +import java.lang.ref.WeakReference; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Filip Hanik + * @version 1.0 + */ +public class PooledConnection { + + public static final int VALIDATE_BORROW = 1; + public static final int VALIDATE_RETURN = 2; + public static final int VALIDATE_IDLE = 3; + public static final int VALIDATE_INIT = 4; + + protected static Log log = LogFactory.getLog(PooledConnection.class); + protected static volatile int counter = 1; + + protected PoolProperties poolProperties; + protected java.sql.Connection connection; + protected String abandonTrace = null; + protected long timestamp; + protected ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false); + protected boolean discarded = false; + protected long lastValidated = System.currentTimeMillis(); + protected int instanceCount = 0; + protected ConnectionPool parent; + + protected WeakReference handler = null; + + public PooledConnection(PoolProperties prop, ConnectionPool parent) throws SQLException { + instanceCount = counter++; + poolProperties = prop; + this.parent = parent; + } + + protected void connect() throws SQLException { + if (connection != null) { + try { + this.disconnect(); + } catch (Exception x) { + log.error("Unable to disconnect previous connection.", x); + } //catch + } //end if + java.sql.Driver driver = null; + try { + driver = (java.sql.Driver) Class.forName(poolProperties.getDriverClassName(), + true, PooledConnection.class.getClassLoader()).newInstance(); + } catch (java.lang.Exception cn) { + log.error("Unable to instantiate JDBC driver.", cn); + throw new SQLException(cn.getMessage()); + } + String driverURL = poolProperties.getUrl(); + String usr = poolProperties.getUsername(); + String pwd = poolProperties.getPassword(); + poolProperties.getDbProperties().setProperty("user", usr); + poolProperties.getDbProperties().setProperty("password", pwd); + connection = driver.connect(driverURL, poolProperties.getDbProperties()); + //set up the default state + if (poolProperties.getDefaultReadOnly()!=null) connection.setReadOnly(poolProperties.getDefaultReadOnly().booleanValue()); + if (poolProperties.getDefaultAutoCommit()!=null) connection.setAutoCommit(poolProperties.getDefaultAutoCommit().booleanValue()); + if (poolProperties.getDefaultCatalog()!=null) connection.setCatalog(poolProperties.getDefaultCatalog()); + if (poolProperties.getDefaultTransactionIsolation()!=DataSourceFactory.UNKNOWN_TRANSACTIONISOLATION) connection.setTransactionIsolation(poolProperties.getDefaultTransactionIsolation()); + + this.discarded = false; + } + + protected void reconnect() throws SQLException { + this.disconnect(); + this.connect(); + } //reconnect + + protected synchronized void disconnect() throws SQLException { + if (isDiscarded()) { + return; + } + setDiscarded(true); + if (connection != null) { + connection.close(); + } + connection = null; + parent.finalize(this); + } + + +//============================================================================ +// com.filip.util.IPoolObject methods +//============================================================================ + + public long getAbandonTimeout() { + if (poolProperties.getRemoveAbandonedTimeout() <= 0) { + return Long.MAX_VALUE; + } else { + return poolProperties.getRemoveAbandonedTimeout()*1000; + } //end if + } + + public boolean abandon() { + try { + disconnect(); + } catch (SQLException x) { + log.error("", x); + } //catch + return false; + } + + protected boolean doValidate(int action) { + if (action == PooledConnection.VALIDATE_BORROW && + poolProperties.isTestOnBorrow()) + return true; + else if (action == PooledConnection.VALIDATE_RETURN && + poolProperties.isTestOnReturn()) + return true; + else if (action == PooledConnection.VALIDATE_IDLE && + poolProperties.isTestWhileIdle()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.isTestOnConnect()) + return true; + else if (action == PooledConnection.VALIDATE_INIT && + poolProperties.getInitSQL()!=null) + return true; + else + return false; + } + + /**Returns true if the object is still valid. if not + * the pool will call the getExpiredAction() and follow up with one + * of the four expired methods + */ + public boolean validate(int validateAction) { + return validate(validateAction,null); + } + + public boolean validate(int validateAction,String sql) { + if (!doValidate(validateAction)) { + //no validation required, no init sql and props not set + return true; + } + + String query = (VALIDATE_INIT==validateAction && (poolProperties.getInitSQL()!=null))?poolProperties.getInitSQL():sql; + + if (query==null) query = poolProperties.getValidationQuery(); + + if (query == null) { + //no validation possible + return true; + } + long now = System.currentTimeMillis(); + if (this.poolProperties.getValidationInterval() > 0 && + (now - this.lastValidated) < + this.poolProperties.getValidationInterval()) { + return true; + } + try { + Statement stmt = connection.createStatement(); + boolean exec = stmt.execute(query); + stmt.close(); + this.lastValidated = now; + return true; + } catch (Exception ignore) { + if (log.isDebugEnabled()) + log.debug("Unable to validate object:",ignore); + } + return false; + } //validate + + /** + * The time limit for how long the object + * can remain unused before it is released + */ + public long getReleaseTime() { + return this.poolProperties.getMinEvictableIdleTimeMillis(); + } + + /** + * This method is called if (Now - timeCheckedIn > getReleaseTime()) + */ + public void release() { + try { + disconnect(); + } catch (SQLException x) { + //TODO + } + + } + + /** + * The pool will set the stack trace when it is check out and + * checked in + */ + + public void setStackTrace(String trace) { + abandonTrace = trace; + } + + public String getStackTrace() { + return abandonTrace; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public void setDiscarded(boolean discarded) { + if (this.discarded && !discarded) throw new IllegalStateException("Unable to change the state once the connection has been discarded"); + this.discarded = discarded; + } + + public void setLastValidated(long lastValidated) { + this.lastValidated = lastValidated; + } + + public void setPoolProperties(PoolProperties poolProperties) { + this.poolProperties = poolProperties; + } + + public long getTimestamp() { + return timestamp; + } + + public boolean isDiscarded() { + return discarded; + } + + public long getLastValidated() { + return lastValidated; + } + + public PoolProperties getPoolProperties() { + return poolProperties; + } + + public void lock() { + if (this.poolProperties.isPoolSweeperEnabled()) { + //optimized, only use a lock when there is concurrency + lock.writeLock().lock(); + } + } + + public void unlock() { + if (this.poolProperties.isPoolSweeperEnabled()) { + //optimized, only use a lock when there is concurrency + lock.writeLock().unlock(); + } + } + + public java.sql.Connection getConnection() { + return this.connection; + } + + public JdbcInterceptor getHandler() { + return (handler!=null)?handler.get():null; + } + + public void setHandler(JdbcInterceptor handler) { + if (handler==null) { + if (this.handler!=null) this.handler.clear(); + } else if (this.handler==null) { + this.handler = new WeakReference(handler); + } else if (this.handler.get()==null) { + this.handler.clear(); + this.handler = new WeakReference(handler); + } else if (this.handler.get()!=handler) { + this.handler.clear(); + this.handler = new WeakReference(handler); + } + } + +} diff --git a/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java b/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java new file mode 100644 index 000000000..9a66536bb --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/ProxyConnection.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool; + +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; +/** + * @author Filip Hanik + */ +public class ProxyConnection extends JdbcInterceptor { + + protected PooledConnection connection = null; + + protected ConnectionPool pool = null; + + public PooledConnection getConnection() { + return connection; + } + + public void setConnection(PooledConnection connection) { + this.connection = connection; + } + + public ConnectionPool getPool() { + return pool; + } + + public void setPool(ConnectionPool pool) { + this.pool = pool; + } + + protected ProxyConnection(ConnectionPool parent, PooledConnection con) throws SQLException { + pool = parent; + connection = con; + } + + public void reset(ConnectionPool parent, PooledConnection con) { + this.pool = parent; + this.connection = con; + } + + public boolean isWrapperFor(Class iface) throws SQLException { + return (iface.isInstance(connection.getConnection())); + } + + + public Object unwrap(Class iface) throws SQLException { + if (isWrapperFor(iface)) { + return connection.getConnection(); + } else { + throw new SQLException("Not a wrapper of "+iface.getName()); + } + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (isClosed()) throw new SQLException("Connection has already been closed."); + if (CLOSE_VAL==method.getName()) { + PooledConnection poolc = this.connection; + this.connection = null; + pool.returnConnection(poolc); + return null; + } + return method.invoke(connection.getConnection(),args); + } + + public boolean isClosed() { + return connection==null || connection.isDiscarded(); + } + + public PooledConnection getDelegateConnection() { + return connection; + } + + public ConnectionPool getParentPool() { + return pool; + } + +} diff --git a/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java b/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java new file mode 100644 index 000000000..b30ae01fd --- /dev/null +++ b/java/org/apache/tomcat/jdbc/pool/interceptor/ConnectionState.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.jdbc.pool.interceptor; + +import java.lang.reflect.Method; + +import org.apache.tomcat.jdbc.pool.ConnectionPool; +import org.apache.tomcat.jdbc.pool.DataSourceFactory; +import org.apache.tomcat.jdbc.pool.JdbcInterceptor; +import org.apache.tomcat.jdbc.pool.PooledConnection; + +/** + * Interceptor that keep track of connection state to avoid roundtrips to the database + * @author fhanik + * + */ + +public class ConnectionState extends JdbcInterceptor { + + protected final String[] readState = {"getAutoCommit","getTransactionIsolation","isReadOnly"}; + protected final String[] writeState = {"setAutoCommit","setTransactionIsolation","setReadOnly"}; + + protected Boolean autoCommit = null; + protected Integer transactionIsolation = null; + protected Boolean readOnly = null; + + public void reset(ConnectionPool parent, PooledConnection con) { + autoCommit = null; + transactionIsolation = null; + readOnly = null; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + boolean read = false; + int index = -1; + for (int i=0; (!read) && i10) { + StringBuffer out = new StringBuffer("\n\tType:"); + out.append(parent.getClass().getName()); + out.append("\n\tCreate/Prepare args:"); + for (int i=0; this.args!=null && i + + +]> + + + &project; + + + Filip Hanik + The Tomcat JDBC Connection Pool + + + + + +
+ +

The JDBC Connection Pool org.apache.tomcat.jdbc.pool + is a replacement or an alternative to the commons-dbcp + connection pool.

+ +

So why do we need a new connection pool?

+ +

Here are a few of the reasons: +

    +
  1. commons-dbcp is single threaded, in order to be thread safe commons-dbcp looks the entire pool, even during query validation
  2. +
  3. commons-dbcp is slow - as the number of logical CPUs grow, the performance suffers, the above point shows that there is not support for high concurrency
  4. +
  5. commons-dbcp is complex, over 60 classes. tomcat-jdbc-pool, is 8 classes, hence modifications for future requirement will require + much less changes.
  6. +
  7. commons-dbcp uses static interfaces. This means you can't compile it with JDK 1.6, or if you run on JDK 1.6/1.7 you will get + NoSuchMethodException for all the methods not implemented, even if the driver supports.
  8. +
  9. The commons-dbcp has become fairly stagnant. Sparse updates, releases, and new feature support.
  10. +
  11. It's not worth rewriting over 60 classes, when something as a connection pool can be accomplished with as a much simpler implementation.
  12. +
  13. Tomcat jdbc pool is a Tomcat module, it depends on Tomcat JULI, a greatly simplified logging framework used in Tomcat
  14. +
+

+ +

Features added over other connection pool implementations +

    +
  1. Support for highly concurrent environments and multi core/cpu systems
  2. +
  3. Dynamic implementation of interface, will support java.sql and javax.sql interfaces for + your runtime environment (as long as your JDBC driver does the same), even when compiled with a lower JDK
  4. +
  5. Validation intervals - we don't have to validate every single time we use the connection, we can do this + when we borrow or return the connection, just not more frequent than an interval we can configure.
  6. +
  7. Run-Once query, a configurable query that will be run only once, when the connection to the database is established. + Very useful to setup session settings, that you want to exist during the entire time the connection is established.
  8. +
  9. Ability to configure custom interceptors. + This allows you to write custom interceptors to enhance the functionality. You can use interceptors to gather query stats, + cache session states, reconnect the connection upon failures, retry queries, cache query results, and so on. + Your options are endless and the interceptors are dynamic, not tied to a JDK version of a java.sql/javax.sql interface.
  10. +
  11. High performance - we will show some differences in performance later on
  12. +
  13. Extremely simple, due to the very simplified implementation, the line count and source file count are very low, compare with c3p0 + that has over 200 source files(last time we checked), Tomcat jdbc has a core of 8 files, the connection pool itself is about half + that.
  14. +
+

+ + +
+ + +
+

To provide a very simple switch to and from commons-dbcp and tomcat-jdbc-pool, + Most attributes are the same and have the same meaning.

+ + + + + +

factory is required, and the value should be org.apache.tomcat.jdbc.poo.DataSourceFactory

+
+ +

Type should always be javax.sql.DataSource

+
+
+
+ + +

These attributes are shared between commons-dbcp and tomcat-jdbc-pool, in some cases default values are different.

+ + + +

(boolean) The default auto-commit state of connections created by this pool. If not set, default is JDBC driver default (If not set then the setAutoCommit method will not be called.)

+
+ + +

(boolean) The default read-only state of connections created by this pool. If not set then the setReadOnly method will not be called. (Some drivers don't support read only mode, ex: Informix)

+
+ + +

(String) The default TransactionIsolation state of connections created by this pool. One of the following: (see javadoc )
+ * NONE
+ * READ_COMMITTED
+ * READ_UNCOMMITTED
+ * REPEATABLE_READ
+ * SERIALIZABLE
+ If not set, the method will not be called and it defaults to the JDBC driver. +

+
+ + +

(String) The default catalog of connections created by this pool.

+
+ + +

(String) The fully qualified Java class name of the JDBC driver to be used. The driver has to be accessible + from the same classloader as tomcat-jdbc.jar +

+
+ + +

(String) The connection username to be passed to our JDBC driver to establish a connection. + Note, at this point, DataSource.getConnection(username,password) is not using the credentials passed into the method. +

+
+ + +

(String) The connection password to be passed to our JDBC driver to establish a connection. + Note, at this point, DataSource.getConnection(username,password) is not using the credentials passed into the method. +

+
+ + +

(int) The maximum number of active connections that can be allocated from this pool at the same time. + The default value is 100

+
+ + +

(int) The maximum number of connections that should be kept in the pool at all times. + Default value is maxActive:100 + Idle connections are checked periodically (if enabled) and + connections that been idle for longer than minEvictableIdleTimeMillis + will be released. (also see testWhileIdle)

+
+ + +

+ (int) The minimum number of established connections that should be kept in the pool at all times. + The connection pool can shrink below this number if validation queries fail. + Default value is derived from initialSize:10 (also see testWhileIdle) +

+
+ + +

(int)The initial number of connections that are created when the pool is started. + Default value is 10

+
+ + +

(long) The maximum number of milliseconds that the pool will wait (when there are no available connections) + for a connection to be returned before throwing an exception. + Default value is 30000 (30 seconds)

+
+ + +

(boolean) The indication of whether objects will be validated before being borrowed from the pool. + If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another. + NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + Default value is false +

+
+ + +

(boolean) The indication of whether objects will be validated before being returned to the pool. + NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + The default value is false. +

+
+ + +

(boolean) The indication of whether objects will be validated by the idle object evictor (if any). + If an object fails to validate, it will be dropped from the pool. + NOTE - for a true value to have any effect, the validationQuery parameter must be set to a non-null string. + The default value is false and this property has to be set in order for the + pool cleaner/test thread is to run (also see timeBetweenEvictionRunsMillis) +

+
+ + +

(String) The SQL query that will be used to validate connections from this pool before returning them to the caller. + If specified, this query does not have to return any data, it just can't throw a SQLException. + The default value is null. + Example values are SELECT 1(mysql), select 1 from dual(oracle), SELECT 1(MS Sql Server) +

+
+ + +

(long) The number of milliseconds to sleep between runs of the idle connection validation/cleaner thread. + This value should not be set under 1 second. It dictates how often we check for idle, abandoned connections, and how often + we validate idle connections. + The default value is 5000 (5 seconds).

+
+ + +

(int) Property not used in tomcat-jdbc-pool.

+
+ + +

(long) The minimum amount of time an object may sit idle in the pool before it is eligable for eviction. + The default value is 60000 (60 seconds).

+
+ + +

(boolean) Property not used. Access can be achieved by calling unwrap on the pooled connection. + see javax.sql.DataSource interface, or call getConnection through reflection.

+
+ + +

(boolean) Flag to remove abandoned connections if they exceed the removeAbandonedTimout. + If set to true a connection is considered abandoned and eligible for removal if it has been in use + longer than the removeAbandonedTimeout Setting this to true can recover db connections from + applications that fail to close a connection. See also logAbandoned + The default value is false.

+
+ + +

(long) Timeout in seconds before an abandoned(in use) connection can be removed. + The default value is 60 (60 seconds). The value should be set to the longest running query your applications + might have.

+
+ + +

(boolean) Flag to log stack traces for application code which abandoned a Connection. + Logging of abandoned Connections adds overhead for every Connection borrow because a stack trace has to be generated. + The default value is false.

+
+ + +

(String) The connection properties that will be sent to our JDBC driver when establishing new connections. + Format of the string must be [propertyName=property;]* + NOTE - The "user" and "password" properties will be passed explicitly, so they do not need to be included here. + The default value is null.

+
+ + +

(boolean) Property not used. The default value is false.

+
+ + +

(int) Property not used. The default value is false.

+
+ + + +
+ +
+ + + + + + +

(String) A custom query to be run when a connection is first created. + The default value is null.

+
+ + +

(String) A semicolon separated list of classnames extending org.apache.tomcat.jdbc.pool.JdbcInterceptor class. + These interceptors will be inserted as an interceptor into the chain of operations on a java.sql.Connection object. + The default value is null.

+
+ + +

(long) avoid excess validation, only run validation at most at this frequency - time in milliseconds. + If a connection is due for validation, but has been validated previously within this interval, it will not be validated again. + The default value is 30000 (30 seconds).

+
+ + +

(boolean) Register the pool with JMX or not. + The default value is true.

+
+
+
+
+ + + + +
-- 2.11.0