2017-02-02 1 views
0

J'ai besoin d'exécuter plusieurs instructions d'initialisation sur chaque connexion empruntée à partir d'un pool JDBC Tomcat. Je ne peux pas utiliser JDBCInterceptors car le pool est partagé avec d'autres applications qui n'auront pas besoin de cette initilisation. J'utilise Spring Boot 1.4.4, déployant sur Tomcat 8.5.11, qui a un ResourceLink dans context.xml à une ressource dans server.xml qui définit le DataSource par rapport à une base de données Oracle 11g. J'accède à DataSource via JNDI.L'aspect échoue avec ClassCastException essayant de pointer Tomcat DataSource getConnection

En réponse à cette question https://stackoverflow.com/a/38746398 Je voulais utiliser Spring AOP pour atteindre mon objectif.

J'ai créé et aspect qui fonctionne parfaitement si le DataSource est défini directement dans context.xml, mais échoue avec un ClassCastException si référencé par un ResourceLink à la même définition dans server.xml

L'exception est la suivante:

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

Ma classe d'image:

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..."); 
    } 
} 

définition ResourceLink dans context.xml:

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

définition DataSource dans server.xml (changé de valeurs privées):

<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"/> 

Comme je l'ai dit, si je définis la même DataSource exacte à context.xml, cela fonctionne parfaitement.

Une idée?

Merci beaucoup.

Répondre

0

Finalement, le problème n'était pas lié à AOP, mais un conflit de dépendance.

Le projet est basé sur Spring Boot 1.4.4.RELEASE. Dans mon build.gradle, j'ai eu la dépendance suivante:

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

Être un démarreur, il est juste un moyen rapide pour obtenir des dépendances plus nécessaires. L'un des DEPS est fourni:

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

qui fournit:

org.apache.tomcat:tomcat-jdbc:8.5.11 

Alors il se trouve que mon paquet de guerre a fini contenant tomcat-jdbc-8.5.11.jar.

Le serveur Tomcat 8.5.11 contenait également la même bibliothèque, tomcat-jdbc.jar.

Comme la piscine était server.xml définie, la bibliothèque utilisée par la piscine était tomcat-jdbc.jar, mais ma demande, ayant tomcat-jdbc-8.5.11.jar l'intérieur de son répertoire WEB-INF/libs, utilisé tomcat-jdbc-8.5.11.jar, conduisant à la ClassCastException. Je n'ai pas eu le temps de creuser plus et trouver la raison réelle pour laquelle cela a fonctionné si le pool a été défini dans context.xml, mais je suppose que c'est juste un cas de priorité de chargement jar.

Le correctif: exclure la tomcat-jdbc-8.5.11.jar du paquet de guerre, en utilisant la gradle enchanment suivante (il suffit d'inclure dans votre section build.gradle configurations):

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

Hope this helps quelqu'un d'autre!