Blog de Antonio Manuel Muñiz

Desarrollo, Ingeniería y Calidad del Software

Archivos en la Categoría: Java

Análisis de un proyecto web Java y Javascript en Sonar

Actualmente existen plugins de Sonar para analizar código Javascript en Sonar, pero hay una limitación, y es que los proyectos en Sonar tienen un lenguaje determinado, por ejemplo, no podemos analizar un proyecto Java y Javascript al mismo tiempo. La única solución (hasta que Sonar permita el análisis de proyectos multi-lenguaje) es crear dos proyectos en Sonar usando el concepto de branch, y configurando dos análisis, uno para el código Java y otro para el código Javascript.

Si el proyecto está modelado con Maven podemos hacer uso de perfiles para definir las dos tareas. Para ello crearemos el siguiente perfil (suponiendo que el código Javascript se encuentre en src/main/webapp/ui):

<profile>
    <id>js</id>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/main/webapp/ui</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

Y lanzaremos dos análisis en Sonar, uno para analizar el código Java:

mvn clean install sonar:sonar -Dsonar.branch=Java

Y otro para analizar el código Javascript:

mvn clean install sonar:sonar -Pjs -Dsonar.branch=Javascript -Dsonar.language=js -Dsonar.dynamicAnalysis=false

Previamente es necesario instalar el plugin de análisis de código Javascript en Sonar (es cuestión de minutos usando el update center). El resultado serán dos proyectos en Sonar. Lo ideal sería un sólo proyecto, pero hasta que Sonar soporte proyectos multi-lenguaje es la única solución.

En la instancia de Clinker que usamos en klicap puedes ver un ejemplo de proyecto Javascript en Sonar.

Anuncios

Monitorización remota en Java: JConsole

Cuando ejecutamos pruebas de carga sobre una aplicación web en un entorno de producción nos enfrentamos a una serie de restricciones:

  • No podemos ejecutar ninguna herramienta en el entorno de producción. Debemos limitarnos a tocar parte de la configuración.
  • No tenemos interfaz gráfica
  • Las condiciones del entorno son “las que son”, no hay posibildad de cambio.

Para la ejecución de las pruebas no hay demasiados problemas, accedemos a modo de cliente, con herramientas como Tsung (comentada aquí hace poco tiempo) o The Grinder (con la que estoy teniendo una estrecha relación ultimamente, y escribiré algo pronto).

Los problemas vienen a la hora de tomar datos de rendimiento de la JVM mientras ejecutamos el load-test, que dadas las condiciones anteriores debemos hacerlo de forma remota. Hay varias herramientas que nos pueden ayudar, como son VisualVM o JConsole (ambas distribuidas con JDK 6). Yo me he quedado con JConsole por una sola razón: en modo remoto me proporciona más información sobre la JVM que VisualVM (para monitorizar en local, no hay duda, VisualVM es la elección).

Para configurar el acceso remoto de JConsole se debe lanzar la máquina virtual a monitorizar con las opciones:

-Djava.rmi.server.hostname=192.168.0.12
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9005
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

Comprobamos desde la máquina cliente (la que usaremos para lanzar JConsole) que el puerto está abierto en la máquina objetivo (en el ejemplo 192.168.0.12):

Antonio$> nmap 192.168.0.12 -p 9005
Starting Nmap 4.53 ( http://insecure.org ) at 2009-06-14 21:26 CEST
Interesting ports on 192.168.0.12:
PORT     STATE SERVICE
9005/tcp open  unknown

NMap done: 1 IP address (1 host up) scanned in 0.144 seconds

Sólo queda iniciar JConsole:

Antonio$> jconsole 192.168.0.12:9005

JConsole monitoriza memoria, CPU, Threads y clases instanciadas, además muestra un dashboard muy útil:

JConsole Dashboard

JConsole Dashboard

JConsole Memory

JConsole Memory

JConsole Classes

JConsole Classes

JConsole Summary

JConsole Summary

JIP Java Profiling

Buscando herramientas para realizar labores de profiling Java he encontrado JIP (Java Interactive Profiler). Esta herramienta permite monitorizar a nivel de máquina virtual parámetros tan útiles como métodos que más tardan en ejecutarse, número de veces que se llama al método o el tiempo que ocupó su ejecución durante la vida del Thread que lo contenía.

Además su configuración es muy sencilla, algo poco común en el mundo del profiling Java. Tan solo es necesario iniciar la máquina virtual con los siguientes parámetros:

-javaagent:/antonio/jip/profile.jar -Dprofile.properties=/antonio/jip/profile.properties

En función del contexto en el que nos movamos estas propiedades se configuran de un modo u otro. Por ejemplo, si estamos monitorizando una aplicación web que es lanzada a través de un wrapper incluiremos entradas en el wrapper.conf, pero si nuestra aplicación está en un Tomcat sólo tenemos que incluirlas en $CATALINA_OPTS. Eso si, las rutas a profile.jar y profile.properties (ambos incluidos en la distribución de JIP) deben ser absolutas.

Al inciar la JVM debemos ver en los logs lo siguiente:

Java Interactive Profiler: starting

Si no, algo hemos hecho mal.

Para iniciar la monitorización haremos uso del cliente de JIP:

./file.sh localhost 15599 /Users/Antonio/temp/jip/jip-1.1.1/client/dump.txt
./start.sh localhost 15599

Esto inciará la sesión de profiling. Cuando hayamos realizado las pruebas oportunas:

./finish.sh localhost 15599

Si todo ha ido bien debemos tener en el directorio seleccionado dos ficheros nuevos: dump.txt y dump.xml. Para visualizar los resultados de una forma más amigable podemos usar jipViewer:

./jipViewer.sh dump.xml

La aplicación Java nos mostrará todos los datos del análisis:

JIP Viewer

JIP Viewer

Para terminar comentar que hay algunos detalles mejorables: la documentación en cuanto a interpretación de los datos es escasa y la aplicación monitorizada va mucho más lenta cuando se está monitorizando. Esto último es un factor común a todas las herramientas de profiling debido al uso de agentes Java que inyectan código para poder realizar los cálculos de tiempo necesarios.

Curiosidades y peculiaridades de Maven y Java

Seguro que todos nos hemos encontrado con ciertas peculiaridades de las herramientas que usamos día a día, pero encontrarse con dos (de las buenas) en un solo día, eso no tiene precio ;) sobre todo cuando el tema te quita medio día.

Son sólo detalles, pero si se dan las condiciones precisas… puede ser un quebradero de cabeza.

Cuando hay dos clases con el mismo FQN (Fully Qualified Name) en el classpath, ¿cuál de las dos se carga? Pues la respuesta es que depende, con Java 1.5 puede ser una, y con Java 1.6 puede ser otra. Resulta que tenía en mi classpath tres versiones de commons-collections (fruto de descontrol con las dependencias transitivas). Si ejecutaba los tests con Java 1.5 todo iba bien, se estaba usando commons-collections-3.1 (que era la versión que yo tanía en mi POM). Pero si usaba Java 1.6 resulta que se cargaba commons-collections-2.1.1 y uno de los métodos que se llamaba por reflexión no existía en esta versión pero sí en la 3.1. La solución, estudiar de donde viene esa dependencia transitiva y excluirla en el POM para que no haya diferentes versiones de un mismo artefacto.

¿Por qué no me aparecen los enlaces a los módulos hijos en el Maven Site? Mi proyecto tenía la estructura típica de un proyecto Maven multimódulo. Configuré los ficheros site.xml para que en el Maven site se pueda navegar mediante enlaces desde el proyecto padre a los hijos y viceversa. Resulta que los enlaces no aparecen. El problema resultó ser que en los POM de los módulos hijos tenía una etiqueta <url> y su contenido era el mismo que en el POM padre, quitando este nodo en los POM de los hijos… problema resuelto. Cuanto menos, curioso.

En fin, el primero de los problemas lo detecté rápido mirando las dependencias transitivas de mi proyecto con Q4E. El segundo fué más bien suerte y gracias alguna incidencia en Codehaus que hablaba de la influencia en el site de la etiqueta <url>.

Memoria PermGen en aplicaciones Java

¿Qué es la memoria PermGen?, de forma resumida se trata de la memoria usada por la máquina virtual Java para almacenar el código estático de las aplicaciónes que corren en ella.

¿Es posible que tengamos problemas con este tipo de memoria?, pues si, sobre todo en aplicaciones web que corren sobre un contenedor JSP/Servlet. La excepción que encontraremos será:

java.lang.OutOfMemoryError: PermGen space

¿Cómo es posible si mi código solo ocupa 1 MB y el límite de memoria PermGen es por defecto 64MB?, si tenemos en cuenta que sólo las librerías usadas por el contenedor pueden ocupar entre 20 y 30 MB, entonces no es tan disparatado. De todas formas seguimos teniendo un margen bastante amplio (unos 30 MB), pero a los 20-30 MB anteriores tenemos que sumarle el tamaño de las librerías de las que depende nuestra aplicación (normalmente en WEB-INF/lib), que si nos descuidamos levemente pueden alcanzar los 20 MB. Llegados a este punto casi hemos alcanzado los 64 MB.

¿Pero la máquina virtual carga el código en memoria bajo demanda?, cierto, aunque hay situaciones en que el código demandado no es solamente el utilizado por nuestra aplicación, un ejemplo claro es el uso de un ORM como Hibernate o un framework como Spring.

¿Cómo puedo monitorizar la memoria PermGen usada por mi aplicación?, con JPS y JSTAT, se trata de dos pequeñas utilidades incluidas en el JDK. En primer lugar debemos conocer el identificador del proceso Java que queremos monitorizar, para ello:

~/Antonio$ jps
  227
  336 WrapperSimpleApp
  340 Jps

En mi caso tengo corriendo una instancia de Plexus corriendo en mi máquina que corresponde al proceso 336. Para consultar la memoria PermGen que esta utilizando:

~/Antonio$ jstat -gcpermcapacity 336
  PGCMN      PGCMX       PGC         PC      YGC   FGC    FGCT     GCT
  8192,0     65536,0     25600,0     25600,0  24     4    0,457    0,866

De todos los datos mostrados nos interesa (en KBytes): la primera columna, tamaño mínimo reservado para la memoria PermGen; la segunda columna, tamaño máximo; tercera columna, memoria ocupada actualmente.

Evidentemente, si PGC (tercera columna) se aproxima a PGCMX debemos incrementar el máximo para la memoria PermGen incluyendo la siguiente opción en la inicialización de la máquina virtual:

-XX:MaxPermSize=256m

En mi caso sería en el script de incialización de Plexus. Es importante notar que la memoria reservada por la máquina virtual inicialmente será la suma de la memoria de datos inicial (heap) y la memoria de instrucciones inicial (PermGen), y similar para la memoria máxima.

Existen herramientas más completas e intuitivas que JSTAT, como Lambda Probe, pero no siempre tenemos la posibilidad de desplegar una aplicación en el contenedor para monitorizar estos parámetros, y mucho menos tocar el script arranque.