Con el hilo de al lado sobre problemas al desplegar dos aplicaciones Struts2 en el mismo Tomcat, un clásico relacionado que merece hilo propio: el infierno de JARs duplicados o incompatibles entre versiones.
El problema típico: pones una librería en $CATALINA_HOME/lib pensando «así está disponible para todas mis aplicaciones sin repetirla en cada WAR», pero si una de esas aplicaciones trae su propia versión distinta de esa misma librería dentro de WEB-INF/lib, empiezas a tener conflictos de classloader difíciles de diagnosticar: NoSuchMethodError, ClassNotFoundException a pesar de que la clase «debería» estar ahí, o comportamientos raros donde parece que se está usando código de una versión antigua sin que tú hayas tocado nada reciente.
La causa de fondo: Tomcat carga las clases de $CATALINA_HOME/lib con un classloader compartido por encima de todas las aplicaciones, y cada WAR tiene su propio classloader hijo para WEB-INF/lib. Si una clase con el mismo nombre existe en ambos sitios, gana el classloader padre (el compartido), así que tu aplicación puede acabar usando silenciosamente la versión de $CATALINA_HOME/lib en vez de la que tú pusiste explícitamente en tu WAR, sin ningún aviso.
Regla práctica que evita el 90% de estos dolores de cabeza: no pongas nada de tu aplicación en $CATALINA_HOME/lib, salvo lo estrictamente necesario a nivel de servidor (drivers JDBC compartidos entre varias apps que sepas con certeza que usan la misma versión compatible). Cada WAR debe traer sus propias dependencias autocontenidas en WEB-INF/lib, para que cada aplicación sea independiente de lo que hagan las demás en el mismo servidor.
¿Alguien ha perdido una tarde entera con uno de estos conflictos de classpath sin saberlo hasta el final? Contad la vuestra.
Nuria
Hay dos enfoques clásicos para esto, con un compromiso distinto cada uno:
1. Carpeta de librerías compartidas de Tomcat ($CATALINA_HOME/lib): cualquier JAR que pongas ahí es visible para todos los WARs desplegados, así que si varias aplicaciones usan la misma versión de una librería común, evitas duplicarla N veces. El problema aparece en cuanto dos aplicaciones necesitan versiones distintas de la misma librería: como el classloader compartido solo puede tener una versión cargada, una de las dos aplicaciones se rompe de forma silenciosa o con errores de NoSuchMethodError/ClassNotFoundException difíciles de diagnosticar si no sabes que viene de ahí.
2. Cada WAR con sus propias dependencias, sin nada en la carpeta compartida: cada aplicación lleva sus JARs en WEB-INF/lib, con su propio classloader aislado. Esto elimina por completo el problema de conflictos de versión entre aplicaciones (cada una vive en su burbuja), a cambio de más espacio en disco duplicado y JARs repetidos entre WARs.
En la práctica, hoy casi todo el mundo va a la opción 2 sin dudarlo: el espacio en disco es barato, y el tiempo perdido depurando un conflicto de versiones entre aplicaciones que comparten classloader no compensa el ahorro. La carpeta compartida de Tomcat solo tiene sentido hoy para cosas realmente transversales que nunca vas a versionar de forma distinta entre aplicaciones (drivers JDBC del propio Tomcat, por ejemplo), no para las dependencias normales de cada aplicación.
Carlos