JDBC Drivers: Classpathhell en Tomcat, Solucion: Geronimo!?

Necesito mejorar la estabilidad de mi conexión a Oracle desde Java mediante JDBC, hasta el momento, todo apunta a que la solución es actualizar la versión de mi driver JDBC. Parece una solución fácil, si no fuera por el Classpathhell que habita dentro de Tomcat, el application server mas usado en el lugar donde trabajo.

Es posible que la solución a mis problemas con el pooling de conexión se encuentre en la utilización de una nueva tecnología de pooling creada por Oracle: El Universal Connection Pooling. Sin embargo, esta tecnología cuenta con una limitación importante, requiere que yo actualice mis drivers de Oracle a la versión 11.1.0.7. Esto no sería un gran problema, si no fuera porque este mismo servidor de Tomcat alberga a muchas aplicaciones, que tienen dependencias con la versión actual del driver JBDC (10.2.x) debido a que Oracle depreco algunas clases en la transición a la versión 11.0. Aunque todas las aplicaciones sufren de este problema con las conexiones, debido a que el problema es intermitente, la mayoría de ellas sobrelleva el problema  con pequeñas interrupciones ocasionales. El usuario está empezando a cansarse de estas interrupciones intermitentes, pero por supuesto las prefiere a la alternativa: Suspender el desarrollo de nuevas aplicaciones y la corrección de errores hasta probar que todas las aplicaciones existentes se comporten correctamente con la nueva versión del driver JDBC.

Ahora bien, el problema parece tener una solución muy simple, todo lo que se tiene que hacer es utilizar una versión distinta del driver JDBC para una de las aplicaciones, ver si en verdad soluciona el problema, y luego migrar progresivamente las aplicaciones a la nueva versión del driver… y seria verdad, si no fuera por un pequeño problema en Tomcat, los drivers JDBC tienen que estar en la carpeta “lib” general de todo el Tomcat para poder ser accesibles mediante JDNI, pero Tomcat no cuenta con una solución para el ClasspathHell.

Que es el ClasspathHell? Bueno, es un problema que existe en Java por que este no cuenta con la capacidad para lidiar con diferentes versiones de un modulo. No existe un modo estándar, en Java, para indicar que tu quieres utilizar la clase FooBar versión 1.0, aunque en el Classpath esten disponibles la version 1.0, la 1.1 y la 1.2. Por lo tanto, si yo copio tanto la versión 10.2 con la version 11.1 dentro de la carpeta lib tomcat, el comportamiento de tomcat sera indefinido, y no podre controlar que version accederá que aplicación.

Una solucion para este problema es el uso de OSGi, que le da a Java un manejo de módulos versionado como el que esta disponible en .NET, desgraciadamente, OSGi requiere que los .jar se construyan con cierta información en el archivo de manifiesto que muchos desarrolladores no acostumbran colocar. Para proyectos opensource existe por supuesto la opción de colocarlos uno mismo, o de utilizar uno de los repositorios gratuitos que distribuye .jars con los manifiesto OSGi correctos. Pero esa no es una opción con los .jar de los drivers jdbc de Oracle que no son opensource.

¿Que hacer entonces? Bueno, hasta que  Java no cuente con una solución a este problema (JSR 277) y las diferentes empresas desarrolladoras no actualicen los manifiestos de sus .jars para ser compatibles con OSGi, parece que la única soluciones instalara otra instancia de Tomcat e ir migrando las aplicaciones a esta nueva instancia con los nuevos drivers.

Esta necesidad de instalara una nueva instancia, me hizo preguntarme si no habría otra solución, por ejemplo, el application server de Oracle: Oc4j cuenta con una solución propietaria llamada “Shared Libraries” con la que se puede clasificar a los .jar por versiones mediante una configuración externa a los .jar y luego especificar que versiones se utilizaran en una aplicación determinada.

Sin embargo, esto implicaría migrar las aplicaciones de Tomcat 6.0 (JDK 1.6) a OC4J (JDK 1.5) lo cual tampoco resulta factible… que hacer entonces?

Tras mucho buscar, descubrí que existe un application server opensource que al parecer cuenta con la capacidad de hacer lo mismo que el comercial OC4J: Apache Geronimo.

Al parecer Geronimo cuenta con la capacidad para manejar diferentes version de .jars compartidos entre las aplicaciones que están ejecutándose dentro de el mediante la característica “Repository”, y como Geronimo puede utilizar a Tomcat como su implementación “Web container” es posible que pueda hacer correr a las aplicaciones en Geronimo si tener alterarlas demasiado.

Por supuesto, todo esto me hace preguntarme… los desarrolladores de Geronimo presumen mucho de su estructura altamente modular…existirá algún modo de tomar solamente el “Repository” de Geronimo, y combinarlo con Tomcat para darle la capacidad de manejar diferentes versiones de .jar compartidos entre aplicaciones?

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

Depende de Tomcat también

Pues no solamente depende de Geronimo sino también de Tomcat... tal vez puedas sacar el puro Repository de Geronimo y usarlo en una aplicación standalone o algo asi, pero para meterlo a Tomcat hay que ver si Tomcat lo va a soportar...

Imagen de luxspes

Si, eso me pregunto... sera

Si, eso me pregunto... sera posible simplemente "reemplazar" el classloader que Tomcat utiliza para leer de su carpeta lib, por el utiliza Geronimo, de forma que se le pueda especificar concretamente que .jars debe cargar?....

Re: JDBC Drivers: Classpathhell en Tomcat, Solucion: Geronimo!?

No mencionas qué problemas tienes de estabilidad en tus conexiones hacia Oracle. Si la razón es que recibes conexiones inválidades desde el pool, sería bueno saber si ya revistaste timeouts para las conexiones en la base de datos, o si estás cacheando demasiados statements en el pool, ejemplo: 10 statements cacheados por cada una de 10 conexiones a la base de datos implican 100 cursores abiertos concurrentemente hacia la base de datos, varios de los cuales pueden estar vencidos al ser retomados del pool.

El problema subyacente con Tomcat, más bien con Catalina, es que para recursos JNDI se requiere utilizar el directorio para shared libraries, pues se levantan dichos recursos con un mismo classloader, antes de cargar las aplicaciones individuales, cada una con su propio classloader.

Tomando en cuenta lo que dices, a menos que sea *una* misma aplicación la que debe acceder a una versión muy moderna de Oracle y a una versión muy antigua de la base de datos, yo lo que haría sería

1. Usar Geronimo.
2. Instalar una, dos, tres, 'n' instancias de Tomcat, cada una con su propia versión del driver JDBC y desplegar las aplicaciones en el nodo que les corresponda.

Por otra parte, ¿de verdad el acuerdo de licencia de los drivers JDBC te impide despanzurrarlos y arreglarles el manifiesto a uno que te acomode?

Otra alternativa sería en lugar de usar el pool de conexiones JNDI de Tomcat, usar Spring (u OSGI) para gestionar el acceso al datasource y su pool de conexiones asociado (pasaríamos de un modelo de service locator a uno de inyeccióon de dependencia, ¿qué más da?). Esto permitiría guardar en eldirectorio lib de cada aplicación el driver con la versión correspondiente.

Por supuesto, todo esto me hace preguntarme… los desarrolladores de Geronimo presumen mucho de su estructura altamente modular…existirá algun modo de tomar solamente el “Repository” de Geronimo, y combinarlo con Tomcat para darle la capacidad de manejar diferentes versiones de .jar compartidos entre aplicaciones?

Si lo harás con tu tiempo libre puede ser interesante hacer dicha adaptación (una solución a la Open EJB o embedded JBoss). Si lo vas a hacer como parte de tu tiempo de trabajo, a menos que la empresa para la que trabajas se dedique a vender infraestructura, creo que lo más ético sería pensar dos veces si de verdad te pagan por desarrollar infraestructura o lógica de negocio (siempre que fueran viables las soluciones más sencillas).

Saludos

Javier Castañón

Imagen de luxspes

El StatementCache esta

El StatementCache esta deshabilitado, asi que no creo que sea por ahi el problema. El error que mas problemas me esta causando es "Se ha encontrado una conexión no válida o anticuada en la caché de conexiones", si tienes alguna pista de como ajustar el pool del driver oracle version 10.2.0.3.0 (pre-UCP) para evitar esto, te lo agradeceria mucho. Otro errores que suelen presentarse, pero con menos frecuencia son: "End of TNS data channel y Connection reset by peer: socket write error", de estos dos sospecho que es un problema con la red o la configuracion de Oracle, pero no he podido establecer concretamente la razon, ante todo me gustaria entender de que manera "varia la falla" y se produce un error (el de TNS) o el otro (el de Connection reset). Y finalmente, a veces, de plano el pool en ves de devolverme un connection, devuelve null.

Les paso un ejemplo de configuracion a ver si se les ocurre algo que pudiera yo ajustar:

<Resource auth="Container"
        connectionCacheProperties="{MinLimit=0, InitialLimit=0, ConnectionWaitTimeout=10, MaxLimit=20}"
        connectionCachingEnabled="true" driverclassname="oracle.jdbc.driver.OracleDriver"
        factory="oracle.jdbc.pool.OracleDataSourceFactory" name="jdbc/jdniname"
        password="XXXX" scope="Shareable" type="oracle.jdbc.pool.OracleDataSource"
        url="jdbc:oracle:thin:@ip:1521:sid" user="XXXX" />

Buscando en Google encontre una opcion para el connectionCacheProperties llamada "ValidateConnection=true", pero supongo que no se trata de informacion fidedigna, por al colocarla no se noto ningun cambio en el comportamiento.

Imagen de luxspes

He estado tentado a

He estado tentado a simplemente crear un decorator del Datasource (extendiendo de DelegatingDataSource) y escribir mi propio codigo de validacion, aunque como dices, es prioridad crear logica de negocios sobre infraestructura (aunque en realidad me pagan para hacer lo que se tenga que hacer para que la aplicaciones funcione correctamente, por lo que no estoy totalmente restringido en ese sentido)
En realidad no quiero embeber todo el Geronimo dentro de Tomcat, solo quisiera ver si puedo tomar la parte del Repository y ajustar a Tomcat para que la use en ves de cargar ciegamente lo que encuentra en lib, y aunque ciertamente estoy consciente de que eso podria ser simplemente demasiado trabajo, quise comentarlo aqui precisamente para ver si alguien en la comunidad conocia algun otro modo de resolver este tipo de problemas de ClasspathHell.
Lo de simplemente manejar al driver jdbc dentro de la aplicacion tambien lo habia pensado, pero (es curioso como se suman las desgracias) los servidores en los que tiene que correr esta aplicacion estan en windows: Que importa, Java es WORA no? Pues resulta que si importa y los .jars de drivers jdbc son especialmente propensos a simplemente "bloquearse" en Windows y hacer imposible el re-deployment de las aplicaciones sin detener completamente a Tomcat (teniendo que afectar a otras aplicaciones, ojala la JVM algun dia lluege a ser MVM, pero mientras tanto...).

Re: El StatementCache esta

La salida de netstat podría ayudar. Si hay conexiones hacia la base de datos en CLOSE_WAIT o TIME_WAIT por ejemplo, indicarían problemas a nivel de interfaz de red, es decir, pudiera ser que ya no nos enfocáramos en el pool sino en TCP/IP, pues es probable que se estén cayendo las conexiones ya establecidas: un driver defectuoso, una configuración inadecuada de las interfaces en un cluster, un firewall intermedio, demasiados ruteadores entre los servidores (se excede el número de brincos y habría que aumentar el TTL para que no expiraran los paquetes), etc.

Por supuesto habrá que revisar primero el timeout para conexiones desde la base de datos o que el listener no se esté cayendo.

La configuración de red de Oracle se guarda en un archivo sqlnet.ora. No olvides revisar la bitácora, está en un archivo sqlnet.log.

Saludos

Javier Castañón

Re: He estado tentado a

¡No puede ser! Las desgracias no suelen venir solas.

El decorador del DataSource no suena mal, y creo que sería menos complicado que el cambio de válvulas en Catalina para agregar el repositorio de Geronimo. Hace muchos años que no veo el código fuente de Catalina, pero recuerdo que quien lo escribió tenía encendido el ofuscador.

Respecto de que se "bloquean los jars" por eso había sugerido el uso de una instancia de Tomcat por cada versión del driver, especulando que los miembros estáticos se cargasen una vez para toda la JVM y existiesen conflictos derivados por ello.

¿Has pensado en Glassfish? A lo mejor también tiene una alternativa, aunque la migración a Gerónimo no suena nada mal.

Saludos y suerte

Javier

Imagen de 1a1iux

Adiós Tomcat

Mmmhh, que interesante problema... mi opinión...

Yo creo que es momento de que empieces a considerar moverte a un sevidor de aplicaciones más robusto, más "de a deberás". No sé hasta que punto sea Tomcat el culpable de las cosas tan tristes que le pasan a tus aplicaciones pero independientemente de esto, repito, para mi lo primero que hay que hacer es una migración.. JBoss, Glassfish, Geronimo, hay de donde escoger.

Lo primero que yo haría es observar como se comportan mis aplicaciones en alguno de estos servidores y ver si tengo la posibilidad (REAL) de tener los JARs dentro de cada aplicación web. Si es así, pues ya terminé, jaja.

Por el análisis que has hecho, parece que Geronimo es la primera opción. Personalmente no le veo mucho sentido querer tomar el Repository de Geronimo y hacerle un merge con el Tomcat.. si te mueves a Geronimo, pues te mueves completo, ¿no?

En fin,

Pues no dejes de plarticarnos cómo te fue, y qué es lo que finalmente hiciste, ok

Sale y vale
Byte

Imagen de ezamudio

JBoss

JBoss puede ser buena opción. A pesar de que el contenedor de servlets de JBoss es nada menos que Tomcat, el manejo de DataSources no es el mismo de Tomcat, tiene otro que a mi parecer se comporta mejor (no he usado JBoss con Oracle pero con PostgreSQL no he tenido ningún problema de los que mencionan).

Imagen de luxspes

No se si sea otro el manejo

No se si sea otro el manejo de DataSource de JBoss, tal ves si (la configuracion definitivamente se hace en un archivo distinto), pero para este caso creo que no ayudaria, por que hasta donde se JBoss todavia no cuenta con capacidad para cargar versiones especificas de .jars compartidos como hacen Geronimo o OC4J. Supestamente Glassfish podria hacerlo mediante OSGi, pero no he podido encontrar ejemplos concretos al respecto en Google