The Tomcat JDBC Connection Pool

Table of Contents

Introduction

The JDBC Connection Pool org.apache.tomcat.jdbc.pool is a replacement or an alternative to the Apache Commons DBCP connection pool.

So why do we need a new connection pool?

Here are a few of the reasons:

  1. Commons DBCP 1.x is single threaded. In order to be thread safe Commons locks the entire pool for short periods during both object allocation and object return. Note that this does not apply to Commons DBCP 2.x.
  2. Commons DBCP 1.x can be slow. As the number of logical CPUs grows and the number of concurrent threads attempting to borrow or return objects increases, the performance suffers. For highly concurrent systems the impact can be significant. Note that this does not apply to Commons DBCP 2.x.
  3. Commons DBCP is over 60 classes. tomcat-jdbc-pool core is 8 classes, hence modifications for future requirement will require much less changes. This is all you need to run the connection pool itself, the rest is gravy.
  4. Commons DBCP uses static interfaces. This means you have to use the right version for a given JRE version or you may see NoSuchMethodException exceptions.
  5. It's not worth rewriting over 60 classes, when a connection pool can be accomplished with a much simpler implementation.
  6. Tomcat jdbc pool implements the ability retrieve a connection asynchronously, without adding additional threads to the library itself.
  7. Tomcat jdbc pool is a Tomcat module, it depends on Tomcat JULI, a simplified logging framework used in Tomcat.
  8. Retrieve the underlying connection using the javax.sql.PooledConnection interface.
  9. Starvation proof. If a pool is empty, and threads are waiting for a connection, when a connection is returned, the pool will awake the correct thread waiting. Most pools will simply starve.

Features added over other connection pool implementations

  1. Support for highly concurrent environments and multi core/cpu systems.
  2. 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 version of the JDK.
  3. 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.
  4. 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.
  5. 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.
  6. High performance - we will show some differences in performance later on
  7. 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. As bugs may occur, they will be faster to track down, and easier to fix. Complexity reduction has been a focus from inception.
  8. Asynchronous connection retrieval - you can queue your request for a connection and receive a Future<Connection> back.
  9. Better idle connection handling. Instead of closing connections directly, it can still pool connections and sizes the idle pool with a smarter algorithm.
  10. You can decide at what moment connections are considered abandoned, is it when the pool is full, or directly at a timeout by specifying a pool usage threshold.
  11. The abandon connection timer will reset upon a statement/query activity. Allowing a connections that is in use for a long time to not timeout. This is achieved using the ResetAbandonedTimer
  12. Close connections after they have been connected for a certain time. Age based close upon return to the pool.
  13. Get JMX notifications and log entries when connections are suspected for being abandoned. This is similar to the removeAbandonedTimeout but it doesn't take any action, only reports the information. This is achieved using the suspectTimeout attribute.
  14. Connections can be retrieved from a java.sql.Driver, javax.sql.DataSource or javax.sql.XADataSource This is achieved using the dataSource and dataSourceJNDI attributes.
  15. XA connection support. XA connections are provided by the data source named by the dataSource or dataSourceJNDI attribute, which must implement javax.sql.XADataSource.

How to use

Usage of the Tomcat connection pool has been made to be as simple as possible, for those of you that are familiar with commons-dbcp, the transition will be very simple. Moving from other connection pools is also fairly straight forward.

Additional features

The Tomcat connection pool offers a few additional features over what most other pools let you do:

  • initSQL - the ability to run an SQL statement exactly once, when the connection is created
  • validationInterval - in addition to running validations on connections, avoid running them too frequently.
  • jdbcInterceptors - flexible and pluggable interceptors to create any customizations around the pool, the query execution and the result set handling. More on this in the advanced section.
  • fairQueue - Set the fair flag to true to achieve thread fairness or to use asynchronous connection retrieval

Inside the Apache Tomcat Container

The Tomcat Connection pool is configured as a resource described in The Tomcat JDBC documentation With the only difference being that you have to specify the factory attribute and set the value to org.apache.tomcat.jdbc.pool.DataSourceFactory

Standalone

The connection pool only has another dependency, and that is on tomcat-juli.jar. To configure the pool in a stand alone project using bean instantiation, the bean to instantiate is org.apache.tomcat.jdbc.pool.DataSource. The same attributes (documented below) as you use to configure a connection pool as a JNDI resource, are used to configure a data source as a bean.

JMX

The connection pool object exposes an MBean that can be registered. In order for the connection pool object to create the MBean, the flag jmxEnabled has to be set to true. This doesn't imply that the pool will be registered with an MBean server, merely that the MBean is created. In a container like Tomcat, Tomcat itself registers the DataSource with the MBean server, the org.apache.tomcat.jdbc.pool.DataSource object will then register the actual connection pool MBean. If you're running outside of a container, you can register the DataSource yourself under any object name you specify, and it propagates the registration to the underlying pool. To do this you would call mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname). Prior to this call, ensure that the pool has been created by calling dataSource.createPool().

The propagation of the registration to the connection pool MBean happens once, at the moment the data source is registered. If the pool has not been created at that point, no connection pool MBean is published, nothing is published later when the pool is created, and the registration is not retried; a warning is logged at registration time. Ensure the pool exists before the data source is registered - in a container the resource factory creates it up front - or deregister and register the data source again afterwards.

Only a registration propagated through the data source is taken back when the data source is closed or deregistered. A connection pool MBean registered directly, as in the call above, is not unregistered by close(); unregister it yourself when you close the data source. (Inside Tomcat the container registers the data source through its own wrapper, and the pool MBean's lifecycle is managed for you.)

Closing the data source

Calling close() or close(false) on the data source closes the connection pool and is terminal: later calls to getConnection() or createPool() fail with a SQLException instead of silently recreating the pool. Connections that are currently borrowed are not disconnected by close(false); they keep their database sessions and stay usable until their holders close them. close(true) additionally disconnects the database sessions of the connections that are still in use. JMX monitoring of the pool ends when close() returns, including for connections that are still in use.

Attributes

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.

When the pool is configured through the JNDI resource factory, an attribute whose value is blank leaves its property at the default: clearing an attribute in a resource definition switches the property off rather than configuring it to the empty value. A value the property cannot take - an unparseable number, an unknown boolean or transaction isolation level - fails the creation of the pool with an error naming the attribute, rather than being silently ignored. Numeric, boolean and keyword values, class names and JNDI names are read ignoring surrounding whitespace; names, queries and URLs are stored verbatim, since a space can be part of such a value.

JNDI Factory and Type

Attribute Description
factory

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

type

Type should always be javax.sql.DataSource or javax.sql.XADataSource

Depending on the type a org.apache.tomcat.jdbc.pool.DataSource or a org.apache.tomcat.jdbc.pool.XADataSource will be created.

Note that the javax.sql.XADataSource type only provides XA connections if the pool is backed by an XA data source: the data source the pool takes its connections from - supplied through the dataSourceJNDI or dataSource attribute - must implement javax.sql.XADataSource. In an XML resource definition an attribute can only carry text, while the dataSource attribute takes the data source object itself, so the XML route to a backing data source is dataSourceJNDI, naming a javax.sql.XADataSource provided by the JDBC driver and bound in JNDI. A pool configured with driverClassName and url pools plain JDBC connections, and a request for an XA connection from such a pool fails with an explicit error.

System Properties

System properties are JVM wide, affect all pools created in the JVM

Attribute Description
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader

(boolean) Controls classloading of dynamic classes, such as JDBC drivers, interceptors and validators. If set to false, default value, the pool will first attempt to load using the current loader (i.e. the class loader that loaded the pool classes) and if class loading fails attempt to load using the thread context loader. Set this value to true, if you wish to remain backwards compatible with Apache Tomcat 8.0.8 and earlier, and only attempt the current loader. If not set then the default value is false.

Common Attributes

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

Attribute Description
defaultAutoCommit

(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.) When the ConnectionState interceptor is configured, it applies this setting on every borrow instead of at connection creation, and re-applies it whenever a borrower has changed it.

defaultReadOnly

(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) When the ConnectionState interceptor is configured, it applies this setting on every borrow instead of at connection creation, and re-applies it whenever a borrower has changed it.

defaultTransactionIsolation

(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

The numeric transaction isolation level of java.sql.Connection, for example 2 for READ_COMMITTED, is accepted in place of the name. The name or number is matched ignoring surrounding whitespace, and any value other than the names above, their numbers or a blank value fails the creation of the pool with an error naming the attribute.

If not set, or set to a blank value, the method will not be called and it defaults to the JDBC driver. When the ConnectionState interceptor is configured, it applies this setting on every borrow instead of at connection creation, and re-applies it whenever a borrower has changed it.

defaultCatalog

(String) The default catalog of connections created by this pool. When the ConnectionState interceptor is configured, it applies this setting on every borrow instead of at connection creation, and re-applies it whenever a borrower has changed it.

driverClassName

(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

username

(String) The connection username to be passed to our JDBC driver to establish a connection. Note that method DataSource.getConnection(username,password) by default will not use credentials passed into the method, but will use the ones configured here. See alternateUsernameAllowed property for more details.

password

(String) The connection password to be passed to our JDBC driver to establish a connection. Note that method DataSource.getConnection(username,password) by default will not use credentials passed into the method, but will use the ones configured here. See alternateUsernameAllowed property for more details.

maxActive

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

maxIdle

(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)

minIdle

(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)

initialSize

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

maxWait

(int) 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)

testOnBorrow

(boolean) The indication of whether objects will be validated before being borrowed from the pool. If the object fails to validate, a reconnection is attempted using the same object. If the reconnection also fails, the object is dropped from the pool and an SQLException is thrown. In order to have a more efficient validation, see validationInterval. Default value is false

testOnConnect

(boolean) The indication of whether objects will be validated when a connection is first created. If an object fails to validate, it will be throw SQLException. Default value is false

testOnReturn

(boolean) The indication of whether objects will be validated before being returned to the pool. The default value is false.

testWhileIdle

(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. 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)

validationQuery

(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 an SQLException. The default value is null. If not specified, or set to a blank value, connections will be validated by the isValid() method of java.sql.Connection: a blank query is not sent to the database as an empty statement. Example values are SELECT 1(mysql), select 1 from dual(oracle), SELECT 1(MS Sql Server)

validationQueryTimeout

(int) The timeout in seconds before a connection validation queries fail. This works by calling java.sql.Statement.setQueryTimeout(seconds) on the statement that executes the validationQuery. The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts. A value less than or equal to zero will disable this feature. The default value is -1.

validatorClassName

(String) The name of a class which implements the org.apache.tomcat.jdbc.pool.Validator interface and provides a no-arg constructor (may be implicit). If specified, the class will be used to create a Validator instance which is then used instead of any validation query to validate connections. The default value is null. An example value is com.mycompany.project.SimpleValidator.

timeBetweenEvictionRunsMillis

(int) 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. This value will be overridden by maxAge if the latter is non-zero and lower. The default value is 5000 (5 seconds).

numTestsPerEvictionRun

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

minEvictableIdleTimeMillis

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

accessToUnderlyingConnectionAllowed

(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 or cast the object as javax.sql.PooledConnection

removeAbandoned

(boolean) Flag to remove abandoned connections if they exceed the removeAbandonedTimeout. 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.

removeAbandonedTimeout

(int) 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.

logAbandoned

(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.

connectionProperties

(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.

poolPreparedStatements

(boolean) Property not used.

maxOpenPreparedStatements

(int) Property not used.

Tomcat JDBC Enhanced Attributes

Attribute Description
initSQL

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

jdbcInterceptors

(String) A semicolon separated list of classnames extending org.apache.tomcat.jdbc.pool.JdbcInterceptor class. See Configuring JDBC interceptors below for more detailed description of syntax and examples.

These interceptors will be inserted as an interceptor into the chain of operations on a java.sql.Connection object. The default value is null.

Predefined interceptors:
org.apache.tomcat.jdbc.pool.interceptor.
ConnectionState
- keeps track of auto commit, read only, catalog and transaction isolation level.
org.apache.tomcat.jdbc.pool.interceptor.
StatementFinalizer
- keeps track of opened statements, and closes them when the connection is returned to the pool.

More predefined interceptors are described in detail in the JDBC Interceptors section.

validationInterval

(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 3000 (3 seconds).

jmxEnabled

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

object_name

(String) The pool's name, used as the name key property of the JMX registrations of the pool's MBean and of interceptors that register with the pool, such as SlowQueryReportJmx. When the attribute is set through the JNDI resource factory, surrounding whitespace is not stripped - the name is stored as it is written - and a blank value leaves the pool with its generated name. Note that this attribute of the pool is spelled with an underscore, unlike the objectName attribute of the SlowQueryReportJmx interceptor described below, which overrides only that interceptor's registration.

When the attribute is left unset on a container-managed JNDI resource whose data source is built by the Tomcat JDBC connection pool factory, the container passes a generated, container-unique name: <domain>/<host>/<context>/<JNDI-name> for a webapp resource and <domain>/<JNDI-name> for a global resource. An explicitly configured value always overrides the container-passed one.

fairQueue

(boolean) Set to true if you wish that calls to getConnection should be treated fairly in a true FIFO fashion. This uses the org.apache.tomcat.jdbc.pool.FairBlockingQueue implementation for the list of the idle connections. The default value is true. This flag is required when you want to use asynchronous connection retrieval.
Setting this flag ensures that threads receive connections in the order they arrive.
During performance tests, there is a very large difference in how locks and lock waiting is implemented. When fairQueue=true there is a decision making process based on what operating system the system is running. If the system is running on Linux (property os.name=Linux. To disable this Linux specific behavior and still use the fair queue, simply add the property org.apache.tomcat.jdbc.pool.FairBlockingQueue.ignoreOS=true to your system properties before the connection pool classes are loaded.

abandonWhenPercentageFull

(int) Connections that have been abandoned (timed out) won't get closed and reported up unless the number of connections in use are above the percentage defined by abandonWhenPercentageFull. The value should be between 0-100. The default value is 0, which implies that connections are eligible for closure as soon as removeAbandonedTimeout has been reached.

maxAge

(long) Time in milliseconds to keep a connection before recreating it. When a connection is borrowed from the pool, the pool will check to see if the now - time-when-connected > maxAge has been reached , and if so, it reconnects before borrow it. When a connection is returned to the pool, the pool will check to see if the now - time-when-connected > maxAge has been reached, and if so, it tries to reconnect. When a connection is idle and timeBetweenEvictionRunsMillis is greater than zero, the pool will periodically check to see if the now - time-when-connected > maxAge has been reached, and if so, it tries to reconnect. Setting maxAge to a value lower than timeBetweenEvictionRunsMillis will override it (so idle connection validation/cleaning will run more frequently). The default value is 0, which implies that connections will be left open and no age check will be done upon borrowing from the pool, returning the connection to the pool or when checking idle connections.

useEquals

(boolean) Set to true if you wish the ProxyConnection class to use String.equals and set to false when you wish to use == when comparing method names. This property does not apply to added interceptors as those are configured individually. The default value is true.

useLock

(boolean) Set to true if a lock should be used when operations are performed on the connection object. Should be set to false unless you plan to have a background thread of your own doing idle and abandon checking, such as a JMX client. If the pool sweeper is enabled - the pool's own background maintenance thread, started by timeBetweenEvictionRunsMillis together with one of the checks it runs - the lock is used regardless of this setting. The default value is false.

suspectTimeout

(int) Timeout value in seconds. Default value is 0.
Similar to to the removeAbandonedTimeout value but instead of treating the connection as abandoned, and potentially closing the connection, this simply logs the warning if logAbandoned is set to true. If this value is equal or less than 0, no suspect checking will be performed. Suspect checking only takes place if the timeout value is larger than 0 and the connection was not abandoned or if abandon check is disabled. If a connection is suspect a WARN message gets logged and a JMX notification gets sent once.

rollbackOnReturn

(boolean) If autoCommit==false then the pool can terminate the transaction by calling rollback on the connection as it is returned to the pool Default value is false.

commitOnReturn

(boolean) If autoCommit==false then the pool can complete the transaction by calling commit on the connection as it is returned to the pool If rollbackOnReturn==true then this attribute is ignored. Default value is false.

alternateUsernameAllowed

(boolean) By default, the jdbc-pool will ignore the DataSource.getConnection(username,password) call, and simply return a previously pooled connection under the globally configured properties username and password, for performance reasons.

The pool can however be configured to allow use of different credentials each time a connection is requested. To enable the functionality described in the DataSource.getConnection(username,password) call, simply set the property alternateUsernameAllowed to true.
Should you request a connection with the credentials user1/password1 and the connection was previously connected using different user2/password2, the connection will be closed, and reopened with the requested credentials. This way, the pool size is still managed on a global level, and not on a per schema level.
The default value is false.
This property was added as an enhancement to bug 50025.

dataSource

(javax.sql.DataSource) Inject a data source to the connection pool, and the pool will use the data source to retrieve connections instead of establishing them using the java.sql.Driver interface. This is useful when you wish to pool XA connections or connections established using a data source instead of a connection string. Default value is null

When the pool is configured through the JNDI resource factory, this attribute carries the data source object itself, and the object must be a javax.sql.DataSource, a javax.sql.XADataSource or a javax.sql.ConnectionPoolDataSource provided by the JDBC driver - and not another connection pool, which the pool refuses to wrap. A resource definition can only give attributes as text, and text cannot carry a live object; a text value here fails the creation of the pool. In a resource definition, use this attribute's JNDI counterpart, dataSourceJNDI, to back the pool with a data source.

dataSourceJNDI

(String) The JNDI name for a data source to be looked up in JNDI and then used to establish connections to the database. See the dataSource attribute. Default value is null. Surrounding whitespace around the name is ignored. If the name cannot be resolved when the pool is created, or resolves to an object the pool cannot establish connections from, the creation fails with an error naming the unresolved name rather than producing a pool that cannot provide connections.

useDisposableConnectionFacade

(boolean) Deprecated. This property no longer has any effect. The pool always puts a facade on the connections it hands out, so that a connection cannot be reused after it has been closed: after close() has been called, all calls on the handle except close() and isClosed() throw an exception. Switching the facade off used to leave a handle usable after close, which allowed it to return or drive the connection of a later borrower; setting this property to false is therefore not supported. The property is still accepted so that existing configurations keep loading.

logValidationErrors

(boolean) Set this to true to log errors during the validation phase to the log file. If set to true, errors will be logged as SEVERE. Default value is false for backwards compatibility.

propagateInterruptState

(boolean) Set this to true to propagate the interrupt state for a thread that has been interrupted (not clearing the interrupt state). Default value is false for backwards compatibility.

ignoreExceptionOnPreLoad

(boolean) Flag whether ignore error of connection creation while initializing the pool. Set to true if you want to ignore error of connection creation while initializing the pool. Set to false if you want to fail the initialization of the pool by throwing exception. The default value is false.

useStatementFacade

(boolean) Deprecated. This property no longer has any effect. The pool always puts a facade on the statements it hands out, so that equals(), hashCode() and toString() can be called on closed statements, Statement.getConnection() reports the pooled connection the statement was created from rather than the driver's own connection, and a statement stops working, along with the result sets read from it, once that connection has been returned to the pool. The handles of that facade, and those of the interceptors that wrap statements beneath it, implement only the standard java.sql interfaces, so the driver's own classes are reached with isWrapperFor() and unwrap(). Switching the facade off used to leave statement and result set handles usable after the borrow they came from had ended, which allowed a kept handle to execute on the session the next borrower held; setting this property to false is therefore not supported. The property is still accepted so that existing configurations keep loading.

Advanced usage

JDBC interceptors

To see an example of how to use an interceptor, take a look at org.apache.tomcat.jdbc.pool.interceptor.ConnectionState. This simple interceptor is a cache of four attributes - transaction isolation level, auto commit, read only state and catalog - in order for the system to avoid not needed roundtrips to the database.

Further interceptors will be added to the core of the pool as the need arises. Contributions are always welcome!

Interceptors are of course not limited to just java.sql.Connection but can be used to wrap any of the results from a method invocation as well. You could build query performance analyzer that provides JMX notifications when a query is running longer than the expected time.

Configuring JDBC interceptors

Configuring JDBC interceptors is done using the jdbcInterceptors property. The property contains a list of semicolon separated class names. If the classname is not fully qualified it will be prefixed with the org.apache.tomcat.jdbc.pool.interceptor. prefix.

Example:
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
is the same as
jdbcInterceptors="ConnectionState;StatementFinalizer"

Interceptors can have properties as well. Properties for an interceptor are specified within parentheses after the class name. Several properties are separated by commas.

Example:
jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)"

Extra whitespace characters around class names, property names and values are ignored.

org.apache.tomcat.jdbc.pool.JdbcInterceptor

Abstract base class for all interceptors, cannot be instantiated.

Attribute Description
useEquals

(boolean) Set to true if you wish the ProxyConnection class to use String.equals and set to false when you wish to use == when comparing method names. The default value is true.

org.apache.tomcat.jdbc.pool.interceptor.ConnectionState

Caches the connection for the following attributes autoCommit, readOnly, transactionIsolation and catalog. It is a performance enhancement to avoid roundtrip to the database when getters are called; setters always reach the driver, and only a successful call updates the cache.

Once this interceptor is configured, the pool also leaves the enforcement of its configured defaults - defaultAutoCommit, defaultReadOnly, defaultTransactionIsolation and defaultCatalog - to it: they are no longer applied at connection creation, the interceptor applies them on every borrow and restores any of them a previous borrower changed, terminating a transaction left open by that borrower first. A restore the driver refuses for the read only, auto commit or catalog state fails the borrow and the pool takes the connection out of service. A refused transaction isolation level is the one exception: the connection stays in service and that level is not retried on it.

Attribute Description

org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer

Keeps track of all statements created using createStatement, prepareStatement or prepareCall and closes these statements when the connection is returned to the pool.

The tracked statements are handed to the application as proxies implementing the standard java.sql statement interfaces, as is the statement facade the pool always installs in front of this interceptor; the driver's own statement class is therefore not reachable by casting. isWrapperFor and unwrap answer through every layer, and unwrap is the supported way to reach driver-specific statement methods. A statement the application unwraps during a borrow is still tracked by this interceptor and is closed, like the others, when the connection is returned to the pool - unless an interceptor listed after this one recycles the statement instead of closing it, as a StatementCache beneath this interceptor does.

The tracking covers the statements created through the pooled connection handle. A statement created on the driver’s own connection – the object Connection.unwrap(Connection.class) hands back – never reaches this interceptor, so it is neither tracked nor closed when the connection is returned to the pool.

Attribute Description
trace

(boolean as String) Enable tracing of unclosed statements. When enabled and a connection is closed, and statements are not closed, the interceptor will log all stack traces. The default value is false.

org.apache.tomcat.jdbc.pool.interceptor.StatementCache

Caches PreparedStatement and/or CallableStatement instances on a connection.

The statements are cached per connection. The count limit is counted globally for all connections that belong to the same pool. Once the count reaches max, subsequent statements are not returned to the cache and are closed immediately.

Note: a cache hit is answered by this interceptor without invoking the rest of the interceptor chain, and recycled statements are reset to driver defaults. Interceptors listed after the cache in jdbcInterceptors do not see reused statements – for example, QueryTimeoutInterceptor must be listed before the cache to keep its timeout on cached statements. An interceptor that tracks statements in order to close them when the connection is returned, such as StatementFinalizer, must also be listed before the cache: a parked statement is deliberately left open, so a tracker beneath the cache would close entries out from under a live cache, and the cache therefore refuses to park statements at all in that order.

Attribute Description
prepared

(boolean as String) Enable caching of PreparedStatement instances created using prepareStatement calls. The default value is true.

callable

(boolean as String) Enable caching of CallableStatement instances created using prepareCall calls. The default value is false.

max

(int as String) Limit on the count of cached statements across the connection pool. The default value is 50.

org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor

See 48392. Interceptor to wrap statements and result sets in order to prevent access to the actual connection using the methods ResultSet.getStatement().getConnection() and Statement.getConnection()

Attribute Description

org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor

Automatically calls java.sql.Statement.setQueryTimeout(seconds) when a new statement is created. The pool itself doesn't timeout the query, it is still up to the JDBC driver to enforce query timeouts.

Note: when combined with org.apache.tomcat.jdbc.pool.interceptor.StatementCache, this interceptor must be listed before the statement cache in the jdbcInterceptors attribute. The cache serves a hit without invoking the rest of the interceptor chain and recycles statements with their query timeout reset to zero, so an interceptor listed after the cache does not see cached statements on reuse and those statements execute with no timeout.

Note: the timeout is applied by an interceptor, so it only covers statements that pass through the interceptor chain. Statements created from a connection reached through XAConnection.getConnection(), PooledConnection.getConnection() or unwrap() are created outside the chain and keep whatever timeout the driver gives them. On a pool backed by an XADataSource the first of these is the normal route an application takes to its connection, so the pool logs a warning at startup when a positive query timeout is configured on such a pool.

Attribute Description
queryTimeout

(int as String) The number of seconds to set for the query timeout. A value less than or equal to zero will disable this feature. The default value is 1 seconds, and omitting the attribute still arms that default: any query that needs longer than one second will be cancelled by the driver, so set the attribute deliberately. The attribute name is matched exactly: an unrecognized name is not applied and is logged as a warning, leaving the default in effect. A value that is not an integer is likewise not applied: the pool logs a warning and the default stays in effect.

org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport

Keeps track of query performance and issues log entries when queries exceed a time threshold or fail. The log level used is WARN

Attribute Description
threshold

(int as String) The number of milliseconds a query has to exceed before issuing a log alert. A negative value (conventionally -1) records every execution as measured and reports none as slow, so nothing is ever logged or notified as a slow query. The default value is 1000 milliseconds.

maxQueries

(int as String) The maximum number of queries to keep track of in order to preserve memory space. A value less than or equal to 0 disables the statistics tracking only; slow and failed query logging remains governed by logSlow and logFailed. The default value is 1000.

logSlow

(boolean as String) Set to true if you wish to log slow queries. The default value is true.

logFailed

(boolean as String) Set to true if you wish to log failed queries. The default value is false.

org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx

Extends the SlowQueryReport and in addition to log entries it issues JMX notification for monitoring tools to react to. Inherits all the attributes from its parent class. This class registers with the JVM's platform MBeanServer, so it works in any JVM, inside or outside a Tomcat container. By default, JMX notifications are sent through the ConnectionPool mbean if it is enabled; with notifyPool=false the SlowQueryReportJmx instead publishes an MBean of its own, one per pool - published on the first connection that pool lends, under the name that pool started with, and given up when that pool closes - and sends the notifications through it.

Attribute Description
notifyPool

(boolean as String) Set to false if you want JMX notifications to go to the SlowQueryReportJmx MBean rather than to the pool's own MBean. Doing so also publishes that interceptor's MBean, described above. The default value is true.

objectName

(String) Define a valid javax.management.ObjectName string that will be used to register this object with the platform mbean server The default value is null and the object will be registered using tomcat.jdbc:type=org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx,name=the-name-of-the-pool Note this interceptor attribute is spelled objectName, unlike the pool's own object_name attribute, which names the pool in the registrations; a objectName attribute on the pool itself is not a pool property and will be reported as ignored. Registration by the default name is collision-free for container-built pools, since a container-managed pool that does not set object_name carries a container-generated unique pool name.

org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer

The abandoned timer starts when a connection is checked out from the pool. This means if you have a 30second timeout and run 10x10second queries using the connection it will be marked abandoned and potentially reclaimed depending on the abandonWhenPercentageFull attribute. Using this interceptor it will reset the checkout timer every time you perform an operation on the connection or attempt to execute a query, whether the attempt succeeds or fails, and on every read of a result set returned through the connection. Iterating a large result set therefore keeps the connection alive through the fetch phase, rather than only until the query returns.

Attribute Description

Code Example

Other examples of Tomcat configuration for JDBC usage can be found in the Tomcat documentation.

Plain Ol' Java

Here is a simple example of how to create and use a data source.

  import java.sql.Connection;
  import java.sql.ResultSet;
  import java.sql.Statement;

  import org.apache.tomcat.jdbc.pool.DataSource;
  import org.apache.tomcat.jdbc.pool.PoolProperties;

  public class SimplePOJOExample {

      public static void main(String[] args) throws Exception {
          PoolProperties p = new PoolProperties();
          p.setUrl("jdbc:mysql://localhost:3306/mysql");
          p.setDriverClassName("com.mysql.jdbc.Driver");
          p.setUsername("root");
          p.setPassword("password");
          p.setJmxEnabled(true);
          p.setTestWhileIdle(false);
          p.setTestOnBorrow(true);
          p.setValidationQuery("SELECT 1");
          p.setTestOnReturn(false);
          p.setValidationInterval(30000);
          p.setTimeBetweenEvictionRunsMillis(30000);
          p.setMaxActive(100);
          p.setInitialSize(10);
          p.setMaxWait(10000);
          p.setRemoveAbandonedTimeout(60);
          p.setMinEvictableIdleTimeMillis(30000);
          p.setMinIdle(10);
          p.setLogAbandoned(true);
          p.setRemoveAbandoned(true);
          p.setJdbcInterceptors(
            "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
            "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
          DataSource datasource = new DataSource();
          datasource.setPoolProperties(p);

          Connection con = null;
          try {
            con = datasource.getConnection();
            Statement st = con.createStatement();
            ResultSet rs = st.executeQuery("select * from user");
            int cnt = 1;
            while (rs.next()) {
                System.out.println((cnt++)+". Host:" +rs.getString("Host")+
                  " User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
            }
            rs.close();
            st.close();
          } finally {
            if (con!=null) try {con.close();}catch (Exception ignore) {}
          }
      }

  }

As a Resource

And here is an example on how to configure a resource for JNDI lookups

<Resource name="jdbc/TestDB"
          auth="Container"
          type="javax.sql.DataSource"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          testWhileIdle="true"
          testOnBorrow="true"
          testOnReturn="false"
          validationQuery="SELECT 1"
          validationInterval="30000"
          timeBetweenEvictionRunsMillis="30000"
          maxActive="100"
          minIdle="10"
          maxWait="10000"
          initialSize="10"
          removeAbandonedTimeout="60"
          removeAbandoned="true"
          logAbandoned="true"
          minEvictableIdleTimeMillis="30000"
          jmxEnabled="true"
          jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
            org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
          username="root"
          password="password"
          driverClassName="com.mysql.jdbc.Driver"
          url="jdbc:mysql://localhost:3306/mysql"/>

Asynchronous Connection Retrieval

The Tomcat JDBC connection pool supports asynchronous connection retrieval without adding additional threads to the pool library. It does this by adding a method to the data source called Future<Connection> getConnectionAsync(). In order to use the async retrieval, two conditions must be met:

  1. You must configure the fairQueue property to be true.
  2. You will have to cast the data source to org.apache.tomcat.jdbc.pool.DataSource
An example of using the async feature is show below.
  Connection con = null;
  try {
    Future<Connection> future = datasource.getConnectionAsync();
    while (!future.isDone()) {
      System.out.println("Connection is not yet available. Do some background work");
      try {
        Thread.sleep(100); //simulate work
      }catch (InterruptedException x) {
        Thread.currentThread().interrupt();
      }
    }
    con = future.get(); //should return instantly
    Statement st = con.createStatement();
    ResultSet rs = st.executeQuery("select * from user");

Interceptors

Interceptors are a powerful way to enable, disable or modify functionality on a specific connection or its sub components. There are many different use cases for when interceptors are useful. By default, and for performance reasons, the connection pool is stateless. The only state the pool itself inserts are defaultAutoCommit, defaultReadOnly, defaultTransactionIsolation, defaultCatalog if these are set. Without a state interceptor these 4 properties are only set upon connection creation; should they be modified during the usage of the connection, the pool itself will not reset them. When the ConnectionState interceptor is configured, it applies the 4 properties on every borrow instead of at connection creation, and restores them whenever a borrower has changed them.

An interceptor has to extend the org.apache.tomcat.jdbc.pool.JdbcInterceptor class. This class is fairly simple, You will need to have a no arg constructor

  public JdbcInterceptor() {
  }

When a connection is borrowed from the pool, the interceptor can initialize or in some other way react to the event by implementing the

  public abstract void reset(ConnectionPool parent, PooledConnection con);

method. This method gets called with two parameters, a reference to the connection pool itself ConnectionPool parent and a reference to the underlying connection PooledConnection con.

When a method on the java.sql.Connection object is invoked, it will cause the

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

method to get invoked. The Method method is the actual method invoked, and Object[] args are the arguments. To look at a very simple example, where we demonstrate how to make the invocation to java.sql.Connection.close() a noop if the connection has been closed

  if (CLOSE_VAL==method.getName()) {
      if (isClosed()) return null; //noop for already closed.
  }
  return super.invoke(proxy,method,args);

There is an observation being made. It is the comparison of the method name. One way to do this would be to do "close".equals(method.getName()). Above we see a direct reference comparison between the method name and static final String reference. According to the JVM spec, method names and static final String end up in a shared constant pool, so the reference comparison should work. One could of course do this as well:

  if (compare(CLOSE_VAL,method)) {
      if (isClosed()) return null; //noop for already closed.
  }
  return super.invoke(proxy,method,args);

The compare(String,Method) will use the useEquals flag on an interceptor and do either reference comparison or a string value comparison when the useEquals=true flag is set.

Pool start/stop
When the connection pool is started or closed, you can be notified. You will only be notified once per interceptor class even though it is an instance method. and you will be notified using an interceptor currently not attached to a pool.

  public void poolStarted(ConnectionPool pool) {
  }

  public void poolClosed(ConnectionPool pool) {
  }

When overriding these methods, don't forget to call super if you are extending a class other than JdbcInterceptor

Configuring interceptors
Interceptors are configured using the jdbcInterceptors property or the setJdbcInterceptors method. An interceptor can have properties, and would be configured like this

  String jdbcInterceptors=
    "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"

Interceptor properties
Since interceptors can have properties, you need to be able to read the values of these properties within your interceptor. Taking an example like the one above, you can override the setProperties method.

  public void setProperties(Map<String, InterceptorProperty> properties) {
     super.setProperties(properties);
     final String myprop = "myprop";
     InterceptorProperty p1 = properties.get(myprop);
     if (p1!=null) {
         setMyprop(Long.parseLong(p1.getValue()));
     }
  }

Getting the actual JDBC connection

Connection pools create wrappers around the actual connection in order to properly pool them. We also create interceptors in these wrappers to be able to perform certain functions. If there is a need to retrieve the actual connection, one can do so using the javax.sql.PooledConnection interface.

  Connection con = datasource.getConnection();
  Connection actual = ((javax.sql.PooledConnection)con).getConnection();

Building

We build the JDBC pool code with 1.6, but it is backwards compatible down to 1.5 for runtime environment. For unit test, we use 1.6 and higher

Other examples of Tomcat configuration for JDBC usage can be found in the Tomcat documentation.

Building from source

Building is pretty simple. The pool has a dependency on tomcat-juli.jar and in case you want the SlowQueryReportJmx

  javac -classpath tomcat-juli.jar \
        -d . \
        org/apache/tomcat/jdbc/pool/*.java \
        org/apache/tomcat/jdbc/pool/interceptor/*.java \
        org/apache/tomcat/jdbc/pool/jmx/*.java

A build file can be found in the Tomcat source repository.

As a convenience, a build file is also included where a simple build command will generate all files needed.

  ant download  (downloads dependencies)
  ant build     (compiles and generates .jar files)
  ant dist      (creates a release package)
  ant test      (runs tests, expects a test database to be setup)

The system is structured for a Maven build, but does generate release artifacts. Just the library itself.