Intercepting calls to a Connection in a spring-managed DataSource
The spring framework provides an easy way for intercepting calls to methods of managed beans. In the project I am currently working at, we wanted to log access to all methods of the Connections returned by a specific datasource. This datasource is defined as a spring bean, but the connections it provides obviously aren’t.
In order to get this done, I decided intercept all calls to the getConnection() method of that specific DataSource an in stead of returning the Connection, I return a dynamic proxy to that connection which will log some method information prior to invoking that method.
Here is the source code and xml config I used to do this…
First we need to implement an interceptor for calls to the DataSource.getConnection() method. This class implements the org.aopalliance.intercept.MethodInterceptor interface:
public class ConnectionCallInterceptor implements MethodInterceptor { public Object invoke(final MethodInvocation methodInvocation) throws Throwable { String methodName = methodInvocation.getMethod().getName(); if ("getConnection".equals(methodName)) { // return proxied connection Connection connection = (Connection) methodInvocation.proceed(); return ConnectionMethodLoggerProxy.newInstance(connection); } else { // simply return methodcall result return methodInvocation.proceed(); } } }
To do the actual logging, all method calls to the java.sql.Connection need to be intercepted, here is the class that creates a dynamic proxy and does some logging in the InvocationHandler implementation:
public class ConnectionMethodLoggerProxy implements InvocationHandler { private Connection connection; public static Connection newInstance(Connection connection) { return (Connection) Proxy.newProxyInstance( connection.getClass().getClassLoader(), new Class[] { Connection.class }, new ConnectionMethodLoggerProxy(connection)); } private ConnectionMethodLoggerProxy(Connection connection) { this.connection = connection; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); String targetName = connection.getClass().getName(); Class[] types = method.getParameterTypes(); String returnTypeName = method.getReturnType().getName(); System.out.println("============================="); System.out.println(" Connection method call detected:"); System.out.println(" - Target : " + targetName); System.out.println(" - Method : " + methodName); System.out.println(" - Arguments : " + ArrayUtils.toString(args)); System.out.println(" - Arg Types : " + ArrayUtils.toString(types)); System.out.println(" - Return type: " + returnTypeName); System.out.println("============================="); return method.invoke(connection, args); } }
Now we have all the code we need, all that is left to do is configure spring to actually use our ConnectionCallInterceptor whenever a method call is made to our datasource (which in this case is a bean named ‘myDataSource’):
<bean class= "org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>myDataSource</value> </list> </property> <property name="interceptorNames"> <list> <value>dataAccessInterceptor</value> </list> </property> <property name="proxyTargetClass" value="false"/> </bean> <bean id="dataAccessInterceptor" class="ConnectionCallInterceptor"/>
Although this is probably not something you’d want to do in a production environment, it helped us a great deal in our specific case. I don’t have a lot of experience with the spring framework, so there may be better ways to do this (and if you know one, please share it :)), but this looked the most straightforward to me.
Thursday 03 May 2007 | Guy Mahieu | aop(t) , java(t) , spring(t)
Generate TODO reports in CruiseControl.NET…
Generate TODO reports in CruiseControl.NET…
its probably alot simpler to have an aspectJ aspect do this and you would have access to alot more information you would be able to log