El problema
En JEE, además de la compilación y empaquetado, comunes en otras disciplinas java, existe un proceso imprescindible y no poco problemático: el despliegue.Los errores de programación suelen ser detectados por el propio compilador y otras herramientas que ayudan a la detección de potenciales errores de ejecución. Sin embargo, hay ciertos errores de despliegue (es decir, errores que se producen en tiempo de despliegue y que te impiden incluso iniciar la aplicación para probarla) que no son fácilmente detectados por ninguna herramienta y que son un verdadero dolor de cabeza para los programadores por sus incomprensibles síntomas y difícil detección.
Existen también otros problemas, no dependientes directamente de nuestro código que se presentan durante el inicio o durante la ejecución de la aplicación. Este tipo de problemas son aún más difíciles de detectar, ya que los síntomas que encontramos no suelen tener ninguna relación con el código asociado al momento/lugar del código donde se producen o donde el servidor señala el error. Este tipo de problemas acostumbra a estar asociado también al despliegue o a algún bug del contenedor.
En este artículo ofrezco una checklist para cuando nos encontremos esas apuradas situaciones, en las que se nos han agotan las posibilidades y no sabemos dónde más mirar o qué más hacer.
Los síntomas
Los síntomas que nos encontramos en este tipo de errores son:- Un artefacto o componente JEE no se comporta como debe o simplemente no funciona en absoluto, sin embargo, el contenedor o servidor no reporta errores. Por ejemplo, un servlet correctamente declarado y desplegado no funciona y no hay errores en los logs.
- El sistema reporta errores que aparentemente no tienen sentido. Por ejemplo, una clase o método que no se encuentra cuando comprobamos que está correctamente desplegado y accesible en el classpath.
- Un segmento determinado de nuestro código no se ejecuta o termina de forma abrupta, y tampoco tenemos errores en los logs. Por ejemplo, un método de una clase implicada en un Resource Adaptor (conector JCA).
El diagnóstico
Se supone que ante los síntomas anteriores, ya hemos agotado todas las posibles opciones y orígenes típicos o supuestos que se nos ocurren, y especialmente, ya hemos descartado del todo (o casi del todo) que la causa sea nuestro propio código. Ante esto, ¿qué hacemos? ¿dónde está el problema? ¿dónde buscar?Pues para esos casos y otros similares, aquí va una checklist que os será de utilidad:
- Revisar nuestro código otra vez, o lo que es mejor, que lo haga otra persona. Incluso en esos casos, lo normal es que el problema siga estando en nuestro código, así que, si lo has revisado dos veces: hazlo una tercera y que la cuarta lo haga otra persona.
- Comprobar que el path de despliegue no tiene caracteres multibyte. Algunas librerías que usan reflection, no están preparadas para cargar clases en paths con espacios o caracteres multibyte (eñes, acentos, etc...). Especialmente en sistemas operativos raros de ésos que algunos usan para desarrollar (como Haselfroch, por ejemplo). Ejemplo: toplink-essentials no funciona desplegado en un path con espacios.
- Cuidado con los despliegues "exploded". En algunas ocasiones nos interesa hacer despliegues desemplaquetados o descomprimidos. Si el directorio del módulo ha sido descomprimido fuera del sistema donde lo vamos a desplegar, puede haberse corrompido durante la copia o transferencia. Hay dos tipos de adulteración que pueden sufrir los ficheros:
- Corrupción binaria. En algunas ocasiones es posible que un fichero .jar (por ejemplo, de nuestras librerías) se corrompa al copiarlo y la aplicación no despliegue por esa causa.
- Alteración de nombres. Es posible, por ejemplo, que si se han copiado de un medio con un filesystem antiguo como FAT32 (case insensitive) acabemos con un directorio META-INF en minúsculas. Este tipo de errores es difícil de encontrar, pero nos crea problemas increíbles. Por ejemplo: la JVM no encontrará las dependencias declaradas en ficheros MANIFEST.MF en directorios meta-inf (así, en minúsculas).
- Revisa los descriptores. Este es uno de esos males provocados por el famoso "copiar y pegar" que tanto daño hace al desarrollo. Por ejemplo, para que funcione el maravilloso mecanismo Dependency Injection de JEE 5 en una aplicación web, es preciso que el descriptor del web.xml especifique expresamente la especificación Servlet 2.5. En definitiva, hay que revisar las cabeceras de nuestros ficheros descriptores y comprobar que ninguno es "heredado" de un proyecto anterior.
- Cuidado con los empaquetados automáticos de los IDE: revisa tus librerías. Cuando utilizamos librerías externas en varios proyectos, éstas suelen tener, además, sus propias dependencias que debemos incluir. Si nuestro empaquetado es automático vía algún IDE puede ocurrir que incluyamos varias veces una misma librería o distintas versiones de las mismas librerías sin darnos cuenta. Si además las librerías se cargan en ClassLoaders distintos, el caos está garantizado. Por ejemplo: este tipo de problemas suele provocar desconcertantes errores con "IllegalArgumentException" o "NoSuchMethodException" ya que el ClassLoader encuentra una versión más antigua de la librería que creemos que está usando.
- Traza, traza, traza. Y hazlo bien: usa log4j o incluso mejor, usa su revisión: logback. Es importante saber en qué punto exactamente el sistema deja de hacer lo esperado.
- Revisa las secciones estáticas de tus clases. Las declaraciones de miembros estáticos o secciones estáticas (static {}) de una clase incluyen muchas veces constructores o llamadas a métodos que pueden, a su vez, generar un NPE (NullPointerException) indeseado. Cuando esto ocurre en una sección estática de una clase, la clase no puede ser inicializada y muchas veces obtenemos mensajes confusos tipo NoCassDefFoundError que nos despistan y no caemos en que todo se debe a un NPE nuestro. En las secciones estáticas, mantén el principio KISS. Por ejemplo, si declaras el típico private static final Pattern pRe = Pattern.compile("\\s*");, asegurate de que la expresión regular es válida o toda la clase entera no podrá usarse en tiempo de ejecución.
- Observa con detalles los logs del servidor y aumenta los niveles de trazado. Es frecuente que no hagamos caso al StackTrace que vuelcan los servidores en sus logs y simplemente nos fijemos en los más bajos de la pila (los últimos en listar). Mira con mayor detalle. Muchas veces te darán pistas sobre dónde buscar. Por ejemplo, me ha pasado escribiendo un Resource Adapter que un método ManagedConnection no se ejecutaba a partir de una determinada línea porque un bug del servidor "mataba" el thread cuando el contenedor llamaba a un método de limpieza. Al aumentar las trazas del contenedor de JCA me dí cuenta del problema (un NPE del contenedor en el dispose() de las conexiones).
Cuando tengas un problema de despliegue y empieces a quedarte sin opciones, vuelve a esta entrada y repasa esta lista de comprobaciones, es posible que te ayude... A mi me sirvió.
No hay comentarios :
Publicar un comentario