Wednesday, April 28, 2010

Un sencillo Add-on de Spring Roo

Ya tenia ganas de meterme un poquito a las entra帽as de Spring Roo para crear un Add-on. Por esa raz贸n estoy escribiendo este peque帽o post sobre como crear un Add-on "sencillito" y carism谩tico.

Empezaremos creando el proyecto de un Add-on con el propio Spring Roo de la siguiente forma:


roo> project --topLevelPackage com.jabaddon.practices.springroo.addons.addonsencillito.roo.addon --template ROO_ADDON_SIMPLE


Como lo pueden notar Spring Roo ya viene con un template de proyecto del tipo Add-on Simple. Es importante mencionar que el paquete del proyecto debe terminar en .roo.addon de lo contrario el propio shell les mostrara un mensaje como el siguiente:


Roo add-ons must have a package name ending in .roo.addon; eg com.mycompany.myproject.roo.addon


¿Y porque debe terminar el paquete en .roo.addon? Debido a que a la hora que inicia el Shell de Spring Roo hace un monitoreo del classpath buscando nuevos Add-ons que cargar y es justamente aquellos paquetes que terminan en .roo.addon los que busca y carga.

Ok, despues de ejecutar la creacion del proyecto la estructura de folders y archivos queda de la siguiente forma:


.
|____legal
| |____LICENSE.TXT
|____log.roo
|____pom.xml
|____readme.txt
|____src
| |____main
| | |____assembly
| | | |____assembly.xml
| | |____java
| | | |____com
| | | | |____jabaddon
| | | | | |____practices
| | | | | | |____roo
| | | | | | | |____addons
| | | | | | | | |____addonsencillito
| | | | | | | | | |____roo
| | | | | | | | | | |____addon
| | | | | | | | | | | |____Commands.java
| | | | | | | | | | | |____Operations.java
| | | | | | | | | | | |____PropertyName.java
| | |____resources
| | | |____META-INF
| | | | |____spring
| | | | | |____applicationContext.xml
| | | | | |____log4j.properties
| | |____webapp
| |____test
| | |____java
| | |____resources


El template del proyecto ha creado 3 clases: Commands.java, Operation.java y PropertyName.java; entre otros archivos.

Si abrimos la clase Commands.java notaremos que es una clase que implementa la interfaz: org.springframework.roo.shell.CommandMarker. Las clases que implementan esta interfaz son aquellas que a la hora de iniciar Spring Roo lee y carga. Ademas, esta marcada con la anotaci贸n @ScopeDevelopmentShell que le indica a Roo que la clase debe ser creada cuando el Shell de desarrollo es usado.

Notaremos tambi茅n que hay m茅todos marcados con la anotaci贸n @CliAvailabilityIndicator. Esta anotaci贸n le indica al Shell de Roo los m茅todos que sirven para indicar si un cierto comando esta disponible o no. Esta anotaci贸n solo aplica para m茅todos p煤blicos que no reciben argumentos y que regresan boolean. Se recomienda que la ejecuci贸n de este m茅todo no sea costosa ya que los m茅todos que indican si un comando esta disponible o no son llamados regularmente.

Los m茅todos marcados con la anotaci贸n @CliCommand son, en si, la ejecuci贸n de un comando. Esta anotaci贸n tiene dos par谩metros: value, que indica el nombre del comando (pueden ser una o mas cadenas) y help, una cadena con un mensaje de ayuda para el comando.

La clase Commands tiene como atributo a la clase Operation (que recibe en el constructor). Esta clase Operations esta marcada con la anotaci贸n @ScopeDevelopment que le indica a Roo que debe instanciar estas clases e inyectarlas a los CommandMaker que tengan constructores que reciban objetos de su tipo.

Basicamente, un Add-on de Spring Roo debe crear clases "Comando" que implementen la interfaz CommandMaker y delegar a objetos "Operaciones" la ejecuci贸n de los mismos.

Ok, dejemos un poco al lado la teor铆a y vayamos un poco mas a la acci贸n. Vamos a instalar el add-on de este proyecto y ver como trabaja.

Para lograr esto ejecutaremos dentro del shell de Roo el comando:


roo> perform assembly


Esto creara un .zip dentro de target el cual es el add-on a instalar con todo lo que necesita (dependencias, recursos, etc.).

Como ejercicio si quieren agreguen dependencias al proyecto, recursos, etc. y ejecuten el "perform assembly" y vean las entra帽as del .zip para que vean como va empaquetado un Add-on.

Ya teniendo el .zip lo instalamos desde el shell de Roo con el comando:


addon install --url file://[ruta hacia el .zip]


Esto copiara el .zip a $ROO_HOME/add-ons (si no funciona el comando copien el archivo directamente).

Ahora si reinician el shell de Roo y corren el comando:


roo> addon clean


tendr谩n la salida:


Loaded com.jabaddon.practices.roo.addons.addonsencillito.roo.addon.Commands; try the 'welcome' commands
____ ____ ____
/ __ \/ __ \/ __ \
/ /_/ / / / / / / /
/ _, _/ /_/ / /_/ /
/_/ |_|\____/\____/ 1.0.0.RELEASE [rev 564]


Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.


Noten el mensaje de la carga del Addon.

¿Quien imprimi贸 ese mensaje? Solo hay que ver el constructor de la clase Commands.


public Commands(StaticFieldConverter staticFieldConverter, Operations operations) {
Assert.notNull(staticFieldConverter, "Static field converter required");
Assert.notNull(operations, "Operations object required");
staticFieldConverter.add(PropertyName.class);
this.operations = operations;
logger.warning("Loaded " + Commands.class.getName() + "; try the 'welcome' commands");
}


Ok, ahora si escribimos "welcome" en el shell y damos "tab" el shell de Roo nos mostrara los comandos disponibles que empiezan con "welcome".


roo> welcome

welcome property welcome write hej welcome write hello


Esos comandos son justamente los declarados en la clase Commands.

Si ejecutamos el comando:


roo> welcome property


Obtendremos como salida:


roo> welcome property
Username : Abaddon


Y si escribimos "welcome property --name" y damos "tab" obtendremos como salida:


roo> welcome property --name

HOME_DIRECTORY USERNAME


Si vemos el c贸digo del m茅todo que atiende el comando veremos que el comando recibe un argumento llamado "name" (especificado con la anotaci贸n @CliOption) el cual es asignado a un objeto del tipo PropertyName. Y si abrimos la clase PropertyName notaremos que tienen dos atributos p煤blicos finales y est谩ticos que son justamente los que se muestran cuando damos "tab".


@ScopeDevelopmentShell
public class Commands implements CommandMarker {

...

@CliCommand(value="welcome property", help="Obtains a pre-defined system property")
public String property(@CliOption(key="name", mandatory=false, specifiedDefaultValue="USERNAME", unspecifiedDefaultValue="USERNAME", help="The property name you'd like to display") PropertyName propertyName) {
return operations.getProperty(propertyName);
}

...
}



public class PropertyName implements Comparable {

private String propertyName;

public static final PropertyName USERNAME = new PropertyName("Username");
public static final PropertyName HOME_DIRECTORY = new PropertyName("Home Directory");

...
}


En fin, el modelo de Add-on de Spring Roo esta muy interesante y espero darme el tiempo para ver en futuros post como acceder a los metadatos de un proyecto Roo como: las entidades existentes, las propiedades que tienen, sus relaciones, etc.

Saturday, April 10, 2010

De Maven a Ant sin dejar Maven

En este peque帽o post vamos a hacer un ejercicio para convertir un proyecto Maven a un proyecto ANT.

En la compa帽铆a donde trabajo (siguenos en twitter @itbrain) usamos como estandar Maven para todos nuestros proyectos ya que hacemos integraci贸n continua, ejecutamos pruebas unitarias y obtenemos reportes del avance del proyecto con ayuda de Maven. Sin embargo, hemos tenido clientes a los cuales tenemos que entregarles los proyectos con scripts en ANT (debido a sus propios procesos) para que estos los puedan construir y empaquetar.

A pesar de que hemos recomendado el uso de Maven a nuestros clientes no es posible cambiar los procesos de estos de la noche a la ma帽ana.

Afortunadamente para nosotros existe el plugin de Ant para Maven (http://maven.apache.org/plugins/maven-ant-plugin/index.html) el cual tiene los fabulosos comandos:


  • ant:ant - Para generar los archivos build de Ant.

  • ant:clean - Para limpiar los archivos build de Ant.



los cuales usaremos en este post para hacer el peque帽o ejercicio de convertir el proyecto con Maven a scripts de ANT.

Comenzamos creando un proyecto simple en maven:

 
mvn archetype:create -DgroupId=com.jabaddon.practices.maven -DartifactId=maven2ant


Terminada la ejecuci贸n de la creaci贸n del proyecto agregaremos unas cuantas dependencias al pom.xml para hacer nuestra prueba:

 

org.springframework
spring
2.5.6


log4j
log4j
1.2.15



Ok, el siguiente paso entonces es ejecutar el comando para crear los scripts de ANT:

 
mvn ant:ant


Al terminar la ejecuci贸n del comando tendremos 3 nuevos archivos en el proyecto:

 
build.xml
maven-build.properties
maven-build.xml


El archivo build.xml b谩sicamente solo importa al archivo maven-build.xml:










El archivo maven-build.xml es el que tiene los "target" de ant que tambi茅n tenemos en Maven y son:


    clean - Para limpiar el proyecto.
    compile - Para compilar el proyecto
    compile-tests - Para compilar las pruebas del proyecto (depende de 'compile').
    test - Para ejecutar todas las pruebas del proyecto.
    javadoc - Para generar el javadoc del Proyecto.
    package - Para empaquetar el JAR, WAR, EAR del Proyecto (depende de 'compile' y 'test').


Y por ultimo el archivo maven-build.propeties tiene definidas propiedades que indican cosas como: la ruta del c贸digo principal, la ruta del c贸digo de pruebas, la ruta hacia las librer铆as, etc.


project.build.outputDirectory=${maven.build.outputDir}
project.build.directory=${maven.build.dir}
maven.test.reports=${maven.build.dir}/test-reports
maven.build.finalName=maven2ant-1.0-SNAPSHOT
maven.reporting.outputDirectory=${maven.build.dir}/site
maven.build.testResourceDir.0=src/test/resources
maven.build.outputDir=${maven.build.dir}/classes
maven.build.resourceDir.0=src/main/resources
maven.build.testOutputDir=${maven.build.dir}/test-classes
maven.repo.local=${user.home}/.m2/repository
maven.settings.offline=false
maven.build.dir=target
maven.settings.interactiveMode=true
maven.build.testDir.0=src/test/java
maven.build.srcDir.0=src/main/java


Y si tuvi茅semos la necesidad de agregar mas "target" en la misma pagina del plugin de Ant recomiendan que se agreguen al archivo build.xml y no al maven-build.xml.