Desarrollando en openmrs con JRebel para realizar cambios en caliente

Cuando se desarrolla una aplicación JavaEE, puede resultar frustrante tener que detener la aplicación cada vez que se haya realizado un cambio en ella, para ser honesto recién estoy comenzando a desarrollar en JavaEE y viniendo del desarrollo en php desde el comienzo me pareció impensable cuando comencé a desarrollar un módulo de OpenMRS esto de tener que detener tomcat y reinstalar la aplicación o utilizar uno de los targets de ant para reiniciar openmrs (exactamente el target update), bueno y así me encontraba durante una semana aproximadamente, cuando derepente llegó un correo de la lista de openmrs para desarrolladores en la que me inscribí y era la solución a éste problema propuesta por Justin Miranda, parte de la conversación se puede ver aquí

Bueno, antes de comenzar con mi adaptación de ésta solución debo comenzar atribuyéndole todo el crédito a Justin Miranda, que además de proponer la solución me ayudó durante varios días mientras aún intentaba hacer que ésto funcione, que no imaginan cuantos problemas tuve, la entrada original se puede encontrar en el blog de Justin:

http://blog.justinmiranda.com/2010/02/hotswap-this.html

Bueno, comenzemos.

Los requisitos:

– OpenMRS, les  recomiendo bajárse la última versión en desarrollo a través de svn de aquí http://svn.openmrs.org/openmrs/trunk/

– Tomcat 5.5 ó 6.0.20 que son las versiones en las que he probado y funciona.

– JRebel http://www.zeroturnaround.com/jrebel/ (es un producto de pago, ya lo sé, pero pueden evaluarlo por 30 días)

– Un módulo de OpenMRS sobre el que realizar las pruebas, les recomiendo éste: http://svn.openmrs.org/openmrs-modules/devexamples/patientnotes/

Entonces comencemos, asumiré que saben como descargar fuentes usando svn, que están sobre windows, y que su estructura de directorios será esta:

D:openmrsopenmrs-trunk <— fuentes de openmrs descargadas por svn

D:openmrspatientnotes <—- modulo de pruebas descargado también por svn

D:JRebel <— ubicación de la instalación de JRebel, en un momento veremos como instalar JRebel

D:tomcat-6.0.20-openmrs <— ubicación de la instalación de JRebel, en un momento veremos como instalar JRebel

Instalación de JRebel:

Será tan sencillo como descargarlo, para Windows lo pueden encontrar aquí: http://www.zeroturnaround.com/downloads/jrebel-setup.exe,

ejecutarlo, indicarle la ruta en la que instalará: “D:JRebel”, y después de que se haya instalado y se inicie el asistente de configuración, no marcar la opción “I run the server from my IDE”, aunque dudo que afecte hacerlo ya JRebel cargará al nivel de ejecución de tomcat y desde allí escaneará nuestros proyectos en busca de cambios.

En un momento determinado se toparán con un plugin de JRebel para eclipse, realmente no hace falta, porque como dije será JRebel al nivel de ejecución de tomcat el que escaneará nuestros proyectos en busca de cambios.

Bueno, ya tenemos JRebel instalado, es hora de configurar tomcat, las indicaciones serán para tomcat 6.0.20, en tomcat 5.5 son idénticas pero puede variar la forma de establecer la variables JAVA_OPTS que es la que modificaremos. Bueno, todo lo que hay que hacer abrir D:tomcat-6.0.20-openmrsbincatalina.bat y modificar para que quede así, aproximadamente a la altura de la línea 78.


rem $Id: catalina.bat 750920 2009-03-06 14:43:19Z markt $
rem ---------------------------------------------------------------------------
set JAVA_OPTS=-Xverify:none -javaagent:D:JRebeljrebel.jar=org.openmrs.util.OpenmrsClassLoader,org.openmrs.module.ModuleClassLoader -Drebel.log=true -Xms512m -Xmx512m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5000 %JAVA_OPTS%

rem Guess CATALINA_HOME if not defined
set CURRENT_DIR=%cd%
if not "%CATALINA_HOME%" == "" goto gotHom

Como se puede ver solo hemos agregado la variable de entorno JAVA_OPTS en la que activamos a Jrebel como agente utilizando los classloaders propios de openmrs, activamos el debugging para la aplicación, lo que será extremadamente útil aunque aquí no se explicará como hacer debugging en openmrs, y además modificamos la memoria asignada a la jvm para evitar problemas de memoria como el permGen, en fin.

Eso es todo dentro de tomcat. Ya podemos iniciarlo usando:

D:tomcat-6.0.20-openmrsbinstartup.bat

para ver los mensajes propios de JRebel, que deben verse aproximadamente así:

Bueno, eso significa que ya no hay nada más que hacer con la configuración de tomcat, ahora hay que configurar la aplicación OpenMRS agregando un archivo rebel.xml dentro de WEB-INFclasses. Podríamos hacerlo después de desplegar la aplicación en tomcat dirigiéndonos a webappsopenmrsWEB-INFclasses y copiando el archivo allí pero yo preferí modificar el build.xml que se encuentra en la raíz de openmrs-trunk para que copie éste archivo en la carpeta classes cuando compile la aplicación, aquí hay otra solución que se está proponiendo como parche para openmrs http://dev.openmrs.org/attachment/ticket/2106/jrebel-build.patch

Bueno, primero crearemos el rebel.xml

rebel.xml


<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://www.zeroturnaround.com"
 xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">

<!-- supervision y actualizacion de clases y xml de configuracion de spring -->

<classpath>
 <dir name="D:openmrsopenmrs-trunkbuild"></dir>

<!--descomentar si estas teniendo problemas del tipo ClassNotFoundException en tomcat *
<jarset dir="D:openmrsopenmrs-trunklibjunit"></jarset>
 <jar name="D:openmrsopenmrs-trunklibspring-frameworkspring-test.jar"></jar>

-->

 <dir name="D:openmrspatientnotesbuild"></dir>

 </classpath>

 <web>

 <link target="WEB-INF/">
 <dir name="D:openmrsopenmrs-trunkwebWEB-INF">
 <exclude name="**/*.xml" />
 </dir>
 </link>

 <link target="WEB-INF/view/module/patientnotes/">
 <dir name="D:openmrspatientnoteswebmodule">
 <include name="**"/> <!-- nunca esta de mas -->
 <exclude name="**/*.xml" />
 </dir>
 </link>

 </web>
</application>

ahora copiamos este archivo dentro de D:openmrsopenmrs-trunkwebWEB-INF

y modificamos D:openmrsopenmrs-trunkbuild.xml a altura de la línea 335 (!) para que quede así:

</pre>
<classes file="${metadata.dir}/api/log4j/log4j.xml" />
 <classes file="${metadata.dir}/api/log4j/velocity.properties" />

 <!-- este es el unico cambio, podria ir en otra parte, notar que es un cambio improvisado, la ubicacion de rebel.xml se deberia configurar de manera externa-->
 <strong><classes file="web/WEB-INF/rebel.xml" /></strong>

 <classes dir="${build.dir}/liquibase-data">
 <include name="*" /> <!-- for the initial setup files: liquibase-schema-only.xml, liquibase-core-data.xml, liquibase-demo-data.xml-->
 </classes>

listo. Llegó la hora de probar, el flujo de instalación de openmrs y el módulo (patientnotes) podría parecer algo extraño, pero es la forma en la que he probado y que al parecer funciona con más frecuencia. Cabe destacar que existen varios factores por lo que derepente nos encontramos con que arrancamos tomcat, y funciona la actualización de jsp´s pero no de clases y pues, si somos observadores podremos detectar por qué suceden éstas cosas y como podemos manejarlas.

1. Iniciar eclipse (asumiré que ya instalaron openmrs-trunk, que lo han iniciado antes y que lo tienen importado en eclipse)

2. Importar proyecto patientnotes a eclipse

3. Activar Project > Build Automatically en eclipse

4. Ejecutar el target package-module del build.xml dentro de patientnotes

5. Copiar  patientnotes/dist/patientnotes-1.0.omod en C:Users ó Documents and setting en XPUsuarioApplication DataOpenMRSmodules

6. Hacer Project > Clean… para el proyecto patientnotes, si no se hace ésto algunas veces JRebel no actualizará clases.

7. Hacer un build dentro de eclipse para los proyecto que JRebel se encuentre manejando (IMPORTANTE, sobre todo si se ha hecho un clean con ant).

8. Iniciar tomcat

9. Reinstalar openmrs ejecutando los targets de ant: clean remove install en ese orden (asumiré que CATALINA_HOME apunta a D:tomcat-6.0.20-openmrs)

Listo, ahora ya podemos modificar controladores, incluso los marcados con anotaciones de spring, crear nuevas clases, modificar jsp’s, etc.

Pero hay un problema, al menos uno que yo he experimentado, las hojas de estilo y javascripts creados o modificados en nuestro proyecto no son sincronizados con el servidor, la solución que he encontrado ahora mismo para este problema es usar el target deploy-web dentro de build.xml de los módulos de openmrs, pero ya que no me gusta ejecutarlo manualmente creé un nuevo Builder de Eclipse, para que, aprovechando que tenemos activado build automatically en eclipse, cada vez que detecte cualquier cambio en nuestro proyecto (un módulo) ejecute deploy-web, no sé si existe una mejor manera de hacerlo, pero postearé esa solución después de experimentar un poco más.

NOTAS:

– He observado que algunas veces JRebel no reconoce los cambios en las clases cuando éste está supervisando muchos archivos, por ejemplo cuando lo configuré para que supervisara los cambios en una carpeta con dojo y muchos archivos dejó de funcionar, aunque no estoy seguro de que haya sido por esa razón.

7 thoughts on “Desarrollando en openmrs con JRebel para realizar cambios en caliente”

  1. Hola,

    Estoy intentando correr JRebel con Tomcat en Windows.

    Me puede ayudar con eso de iniciar openmrs-trunk e importarlo en eclipse?

    Lo tengo importado y corro package-web de openmrs-trun/build.xml, lo que me genera un war con el archivo rebel.xml en WEB-INF y WEB-INF/classes dentro del war.

    Ese war lo pego en tomcat_home/webapps y el omod de mi modulo lo pego en home/Application Data/ OpenMRS/modules.

    En la consola del servidor sale una excepción algo asi:


    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘activeListService’ defined in class path resource [applicationContext-service.xml]: Cannot resolve reference to bean ‘activeListServiceTarget’ while setting beanproperty ‘target’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name

    Si alcanza a aparecer que JRebel monitoreara varios archivos.

    La verdad la excepcion es larguisima y no se cual pueda ser el error.

  2. Hola David, si no me equivoco el proyecto de openmrs ahora esta soportando una configuracion de JRebel para correr la aplicación utilizando jetty y maven, sin la necesidad de configurar el archivo rebel.xml. La última vez que probé hacer eso funcionaba bastante bien. Sin embargo, ese error que recibes me parece que es el que generaba la version 3.6 de jrebel, prueba con versiones anteriores como la 3.5 o la 3 de jrebel, creo que la 3 funciona.

  3. Y puedo tener mi proyecto con la estructura anterior, o debo pasar a la forma maven?

    Me funciono desde hace dos dias y volvio a fallar hoy, me falla cuando cargo mi modulo. Bueno, voy a intentar usar todo lo nuevo, a ver como me va.

  4. Iba a la mitad del camino y me di cuenta que las configuraciones de jrebel son las que ya tengo, actualice eclipse, subclipse y los plugins de maven.

    Pero voy a seguir usando la copia de OpenMRS y la instalacion de jRebel que ya tengo. Tal vez cambiando la configuracion de jRebel funcione otra vez.

  5. Hola de nuevo,

    Ya me funciono, tenia un error tipografico en mi modulo de openmrs en el archivo de mapeo de hibernate y en una clase entidad :p.

    Otra cosa, usted sabe como podria configurar jrebel para actualizar tambien el contenido del archivo de mapeo de hibernate?

Leave a Reply

Your email address will not be published. Required fields are marked *