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. These 4 properties are only set upon connection creation. Should these properties be modified during the usage of the connection,
the pool itself will not reset 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()));
}
}