0

I need to execute several initialization statements on each borrowed connection from a Tomcat JDBC Pool. I cannot use JDBCInterceptors because pool is shared with other applications that won't need said initilization.

I'm using Spring Boot 1.4.4, deploying on Tomcat 8.5.11, which has a ResourceLink in context.xml to a Resource in server.xml that defines the DataSource against a Oracle 11g Database. I'm accessing the DataSource via JNDI.

As per this answer https://stackoverflow.com/a/38746398 I wanted to use Spring AOP to accomplish my goal.

I have created and Aspect that works perfectly if the DataSource is defined directly in context.xml, but fails with a ClassCastException if referenced via a ResourceLink to the same definition in server.xml

The exception is:

java.lang.ClassCastException: org.apache.tomcat.jdbc.pool.DataSource cannot be cast to org.apache.tomcat.jdbc.pool.DataSourceProxy at org.apache.tomcat.jdbc.pool.DataSourceProxy$$FastClassBySpringCGLIB$$26808f96.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) at org.apache.tomcat.jdbc.pool.DataSource$$EnhancerBySpringCGLIB$$17f85659.getConnection(<generated>) 

My Aspect class:

import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { private static final Logger LOG = LoggerFactory.getLogger(DataSourceAspect.class); @AfterReturning(pointcut = "execution(* org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection())") public void afterConnectionEstablished() { LOG.info("Borrowed connection from the pool. Initializing..."); } } 

ResourceLink definition in context.xml:

<ResourceLink name="jdbc/us_j2eeCoreDS" global="jdbc/us_j2eeCoreDS" type="javax.sql.DataSource"/> 

DataSource definition in server.xml (changed private values):

<Resource name="jdbc/us_j2eeCoreDS" type="javax.sql.DataSource" global="jdbc/us_j2eeCoreDS" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" driverClassName="oracle.jdbc.OracleDriver" username="xxx" password="xxx" initialSize="1" minIdle="1" maxIdle="4" maxWaitMillis="5000" maxActive="40" removeAbandonedOnBorrow="true" removeAbandonedTimeout="300" testWhileIdle="true" validationQuery="SELECT 1 FROM DUAL" timeBetweenEvictionRunsMillis="60000" url="jdbc:oracle:thin:@host:port:SID"/> 

As I said before, if I define the exact same DataSource in context.xml, it works flawlessly.

Any clue?

Thanks a lot.

1 Answer 1

0

Eventually the problem was not AOP related, but a dependency conflict.

The project is based on Spring Boot 1.4.4.RELEASE. In my build.gradle, I had the following dependency:

compile("org.springframework.boot:spring-boot-starter-data-jpa") 

Being a starter, it's just a quick way to get more needed dependencies. One of the deps provided is:

org.springframework.boot:spring-boot-starter-jdbc:1.4.4.RELEASE 

Which provides:

org.apache.tomcat:tomcat-jdbc:8.5.11 

So as it turns out my war package ended up containing tomcat-jdbc-8.5.11.jar.

Tomcat 8.5.11 server also contained the same library, tomcat-jdbc.jar.

As the pool was server.xml defined, the library used by the pool was tomcat-jdbc.jar, but my application, having tomcat-jdbc-8.5.11.jar inside its WEB-INF/libs directory, used tomcat-jdbc-8.5.11.jar, leading to the ClassCastException. I didn't have time to dig out more and find the actual reason why it worked if the pool was defined in context.xml, but I guess it's just a case of jar loading priority.

The fix: exclude the tomcat-jdbc-8.5.11.jar from the war package, using the following gradle enchanment (just include it in your build.gradle configurations section):

configurations { runtime.exclude module: 'tomcat-jdbc' } 

Hope this helps somebody else!

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.