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.

Thursday, February 11, 2010

Relación One-to-Many en Spring Roo

En un pasado post hable un poco de los primeros pasos con Sprign Roo.

En este post hare un muy pequeño proyecto con Spring Roo pero me enfocare en ver como hacer entidades con relaciones de uno a muchos (maestro-esclavo).

Ok, pues empecemos.

Primero crearemos el proyecto indicándole el paquete raíz del mismo.


roo> project --topLevelPackage prueba.springroo.relonetomany --projectName relacion-onetomany
Created D:\Abaddon\Projects\JAbaddon\Code\SpringRoo\RelacionOneToMany\pom.xml
Created SRC_MAIN_JAVA
Created SRC_MAIN_RESOURCES
Created SRC_TEST_JAVA
Created SRC_TEST_RESOURCES
Created SRC_MAIN_WEBAPP
Created SRC_MAIN_RESOURCES\META-INF\spring
Created SRC_MAIN_RESOURCES\META-INF\spring\applicationContext.xml
Created SRC_MAIN_RESOURCES\META-INF\spring\log4j.properties


Ahora hay que definir el método de persistencia para el proyecto. Usaremos MySQL con Hibernate.


prueba.springroo.relonetomany roo> persistence setup --provider HIBERNATE --database MYSQL --databaseName rel_one_many
Created SRC_MAIN_RESOURCES\META-INF\persistence.xml
Created SRC_MAIN_RESOURCES\META-INF\spring\database.properties
please enter your database details in src/main/resources/database.properties
Managed SRC_MAIN_RESOURCES\META-INF\spring\applicationContext.xml
Managed ROOT\pom.xml
Managed SRC_MAIN_RESOURCES\META-INF\spring\database.properties


El nombre de base de datos que usamos fue "rel_one_many" así que hay que asegurarnos de tener una base de datos con ese nombre en nuestro MySQL.

En el comando "persistence setup" tambien se puede especificar el usuario y password que usaremos para conectarnos a la base de datos con "--userName" y "--password" respectivamente. Como aqui no lo especificamos asi sera necesario especificarlo en el archivo "src/main/resources/database.properties" para que podamos conectarnos a la base de datos sin problemas.

Lo siguiente sera configurar el logging a un nivel de DEBUG.


prueba.springroo.relonetomany roo> logging setup --level DEBUG
Managed SRC_MAIN_RESOURCES\META-INF\spring\log4j.properties


Ahora para propositos de este ejemplo usaremos usaremos las entidades Grupo y Alumno para representar una relación de uno a muchos donde el Grupo tiene uno o mas Alumnos.

Empecemos creando la entidad Grupo con las propiedades: nombre y fecha de registro.


prueba.springroo.relonetomany roo> entity --class prueba.springroo.relonetomany.dominio.Grupo --testAutomatically
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo.java
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoDataOnDemand.java
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoIntegrationTest.java
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_Entity.aj
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_ToString.aj
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_Configurable.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoIntegrationTest_Roo_Configurable.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoDataOnDemand_Roo_DataOnDemand.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoIntegrationTest_Roo_IntegrationTest.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoDataOnDemand_Roo_Configurable.aj
~.dominio.Grupo roo> field string --fieldName nombre --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo.java
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_ToString.aj
~.dominio.Grupo roo> field date --fieldName fechaRegistro --type java.util.Date --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo.java
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\GrupoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_ToString.aj


La siguiente entidad que crearemos sera Alumno, el cual tendrá las propiedades: nombre, apellido, fecha de nacimiento y numero de credencial.


~.dominio.Grupo roo> entity --class prueba.springroo.relonetomany.dominio.Alumno --testAutomatically
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno.java
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand.java
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoIntegrationTest.java
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_Entity.aj
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_ToString.aj
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_Configurable.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoIntegrationTest_Roo_Configurable.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_DataOnDemand.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoIntegrationTest_Roo_IntegrationTest.aj
Created SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_Configurable.aj
~.dominio.Alumno roo> field string --fieldName nombre --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno.java
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_ToString.aj
~.dominio.Alumno roo> field date --fieldName fechaNacimiento --type java.util.Date --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno.java
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_ToString.aj
~.dominio.Alumno roo> field number --fieldName numeroBoleta --type java.lang.Long --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno.java
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_ToString.aj
~.dominio.Alumno roo> field string --fieldName apellido --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno.java
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_ToString.aj


Ya teniendo las dos entidades ahora vamos a relacionarlas. Lo primero que haremos sera indicar que la entidad Grupo tendrá muchos Alumnos con el comando "field set":


~.dominio.Alumno roo> field set --fieldName alumnos --element prueba.springroo.relonetomany.dominio.Alumno --class prueba.springroo.relonetomany.dominio.Grupo --mappedBy grupo
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo.java
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Grupo_Roo_ToString.aj


Hay que notar aquí que en el momento en que escribimos el comando Spring Roo estaba bajo el contexto de la entidad Alumno (noten la cadena que hay antes del promt de "roo>" y que dice "~.dominio.Alumno"), por esa razón es necesario usar la opción "--class" para indicarle a que clase se le agregara el campo que se esta creando.

Otro punto en el que hay que poner atención es la opción "--mappedBy" la cual la nombramos como "grupo". Esto le dice a Spring Roo que la relación one-to-many estará mapeada por la propiedad "grupo" en los elementos que están del lado "many" de la relación (los contenidos).

Ahora en con la entidad Alumno hacemos la relación hacia el Grupo usando "field reference":


~.dominio.Grupo roo> field reference --fieldName grupo --class prueba.springroo.relonetomany.dominio.Alumno --type prueba.springroo.relonetomany.dominio.Grupo --notNull
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno.java
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_JavaBean.aj
Managed SRC_TEST_JAVA\prueba\springroo\relonetomany\dominio\AlumnoDataOnDemand_Roo_DataOnDemand.aj
Managed SRC_MAIN_JAVA\prueba\springroo\relonetomany\dominio\Alumno_Roo_ToString.aj


La opción "--type" indica el tipo de la referencia que estamos creando en la entidad.

Ok, las relaciones ya están configuradas. Ahora vamos a crear los controladores web con el comando "controller all" (para crear todos los controladores de cada una de las entidades) dentro del paquete "prueba.springroo.relonetomany.web"


~.dominio.Alumno roo> controller all --package prueba.springroo.relonetomany.web
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\web
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\web\GrupoController.java
Created SRC_MAIN_WEBAPP\WEB-INF\spring
Created SRC_MAIN_WEBAPP\WEB-INF\spring\webmvc-config.xml
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\web\GrupoController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP\images
Created SRC_MAIN_WEBAPP\images\nl.png
Created SRC_MAIN_WEBAPP\images\update.png
Created SRC_MAIN_WEBAPP\images\es.png
Created SRC_MAIN_WEBAPP\images\delete.png
Created SRC_MAIN_WEBAPP\images\add.png
Created SRC_MAIN_WEBAPP\images\resultset_previous.png
Created SRC_MAIN_WEBAPP\images\resultset_next.png
Created SRC_MAIN_WEBAPP\images\favicon.ico
Created SRC_MAIN_WEBAPP\images\banner-graphic.png
Created SRC_MAIN_WEBAPP\images\resultset_last.png
Created SRC_MAIN_WEBAPP\images\gb.png
Created SRC_MAIN_WEBAPP\images\springsource-logo.png
Created SRC_MAIN_WEBAPP\images\it.png
Created SRC_MAIN_WEBAPP\images\show.png
Created SRC_MAIN_WEBAPP\images\sv.png
Created SRC_MAIN_WEBAPP\images\list.png
Created SRC_MAIN_WEBAPP\images\resultset_first.png
Created SRC_MAIN_WEBAPP\images\de.png
Created SRC_MAIN_WEBAPP\styles
Created SRC_MAIN_WEBAPP\styles\standard.css
Created SRC_MAIN_WEBAPP\styles\alt.css
Created SRC_MAIN_WEBAPP\WEB-INF\classes
Created SRC_MAIN_WEBAPP\WEB-INF\classes\standard.properties
Created SRC_MAIN_WEBAPP\WEB-INF\classes\alt.properties
Created SRC_MAIN_WEBAPP\WEB-INF\layouts
Created SRC_MAIN_WEBAPP\WEB-INF\layouts\default.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\layouts\layouts.xml
Created SRC_MAIN_WEBAPP\WEB-INF\views
Created SRC_MAIN_WEBAPP\WEB-INF\views\views.xml
Created SRC_MAIN_WEBAPP\WEB-INF\views\resourceNotFound.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\index.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\uncaughtException.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\dataAccessFailure.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\controller-index.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\tags
Created SRC_MAIN_WEBAPP\WEB-INF\tags\language.tagx
Created SRC_MAIN_WEBAPP\WEB-INF\tags\theme.tagx
Created SRC_MAIN_WEBAPP\WEB-INF\tags\pagination.tagx
Created SRC_MAIN_WEBAPP\WEB-INF\i18n
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\messages_sv.properties
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\messages.properties
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\messages_nl.properties
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\messages_it.properties
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\messages_es.properties
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\messages_de.properties
Created SRC_MAIN_WEBAPP\WEB-INF\i18n\application.properties
Managed SRC_MAIN_WEBAPP\WEB-INF\i18n\application.properties
Created SRC_MAIN_WEBAPP\WEB-INF\views\grupo
Created SRC_MAIN_WEBAPP\WEB-INF\views\grupo\list.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\grupo\show.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\grupo\create.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\menu.jspx
Managed SRC_MAIN_WEBAPP\WEB-INF\i18n\application.properties
Managed SRC_MAIN_WEBAPP\WEB-INF\views\menu.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\grupo\update.jspx
Managed SRC_MAIN_WEBAPP\WEB-INF\i18n\application.properties
Managed SRC_MAIN_WEBAPP\WEB-INF\views\menu.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\grupo\views.xml
Created SRC_MAIN_WEBAPP\WEB-INF\urlrewrite.xml
Created SRC_MAIN_WEBAPP\WEB-INF\web.xml
Managed SRC_MAIN_WEBAPP\WEB-INF\web.xml
Managed ROOT\pom.xml
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\web\AlumnoController.java
Managed SRC_MAIN_WEBAPP\WEB-INF\web.xml
Managed ROOT\pom.xml
Created SRC_MAIN_JAVA\prueba\springroo\relonetomany\web\AlumnoController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP\WEB-INF\views\alumno
Created SRC_MAIN_WEBAPP\WEB-INF\views\alumno\list.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\alumno\show.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\alumno\create.jspx
Managed SRC_MAIN_WEBAPP\WEB-INF\i18n\application.properties
Managed SRC_MAIN_WEBAPP\WEB-INF\views\menu.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\alumno\update.jspx
Managed SRC_MAIN_WEBAPP\WEB-INF\i18n\application.properties
Managed SRC_MAIN_WEBAPP\WEB-INF\views\menu.jspx
Created SRC_MAIN_WEBAPP\WEB-INF\views\alumno\views.xml


Ahora hay que probar todo el código para ver si todo esta bien. Recuerden crear la base de datos, configurar el usuario y password de la base de datos y sobre todo levantar la base de datos para que las pruebas se puedan conectar y ejecutar correctamente. Entonces, para ejecutar las pruebas usamos el comando:


~.web roo> perform tests


el cual nos debe indicar, entre otras cosas, un mensaje "BUILD SUCCESSFUL" y si no fue asi algo hicimos mal (asi que hay que volver a empezar desde el principio de este post).

Para ver todo esto corriendo ya en Tomcat simplemente hay que salirnos de la consola de Spring Roo y en el mismo folder del proyecto ejecutar el comando de maven:


$ mvn tomcat:run


Una vez que este corriendo Tomcat hay que ir a la direccion http://localhost:8080/relacion-onetomany/ y naveguen en la pequeña aplicación para que vean la funcionalidad de la relación one-to-many con Spring Roo.



Notaran que al crear Alumnos si no hay Grupos registrados no les muestra la propiedad para indicar el Grupo al que pertenece el Alumno, pero si crean Grupos y después crean Alumnos ya tienen la posibilidad de seleccionar a que grupo va a pertenecer el Alumno que estén creando.

Tuesday, February 9, 2010

Ruby Básico - Manejo de Cadenas

En el anterior post sobre Ruby Básico se hablo de una muy breve introducción a Ruby.

En este post profundizaré en el manejo de cadenas con Ruby.

Como se comento antes se puede crear una cadena en Ruby con comillas simples o comillas dobles. Por ejemplo si creamos un archivo llamado "cadenas00.rb" con el siguiente código:

cadena1 = "Esta es una cadena con comillas dobles"
cadena2 = 'Esta es una cadena con comillas simples'
puts cadena1
puts cadena2

y si lo corremos con Ruby:

$ ruby cadenas00.rb
Esta es una cadena con comillas dobles
Esta es una cadena con comillas simples

Sin embargo, también es posible crear cadenas que este delimitadas por un par de caracteres iguales (que no sean letras) que estén precedidos por el símbolo %. Por ejemplo, si creamos un archivo llamado "cadenas01.rb" con el siguiente código:

puts %!Esta es una cadena!
puts %{Esta es otra cadena}
puts %[Esta también es una cadena]
puts %-Esta sera una cadena?-
puts %!Esta es una cadena como el primer ejemplo pero usando el caracter \! con secuencia de escape!

tenemos como salida:

$ ruby cadenas01.rb
Esta es una cadena
Esta es otra cadena
Esta también es una cadena
Esta sera una cadena?
Esta es una cadena como el primer ejemplo pero usando el caracter ! con secuencia de escape

Este estilo para crear cadenas en Ruby es muy útil para cuando se crean cadenas con mucho texto.

Uno de los muchos usos que les damos a las cadenas en nuestros programas es para imprimir valores de variables. Ruby nos permite hacer sustitucion de expresiones dentro de una cadena simplemente encerrando nuestra expresión entre #{ y } dentro de la cadena misma. Por ejemplo, si creamos un archivo llamado "cadenas02.rb" con el siguiente código:

x = 2
y = "expresión"
z = 2.3

puts "El valor de x es #{x}"
puts "El valor de y es #{y}"
puts "El valor de z es #{z}"

# Sera lo mismo que lo siguiente?

puts "El valor de x es " + x
puts "El valor de y es " + y
puts "El valor de z es " + z

tenemos como salida:

$ ruby cadenas02.rb
El valor de x es 2
El valor de y es expresión
El valor de z es 2.3
cadenas02.rb:11:in `+': can't convert Fixnum into String (TypeError)
from cadenas02.rb:11:in `
'

Notaran que Ruby con el uso de #{} hace automáticamente la transformación adecuada de las variables para poder meterlas a las cadenas.

Como ya había comentado Ruby es un lenguaje orientado a objetos puro de tal forma que una cadena entre comillas es un objeto y se pueden llamar métodos de la clase String. Por ejemplo, si creamos un archivo llamado "cadenas03.rb" con el siguiente código:

cadena1 = "MAYUSCULAS".downcase
cadena2 = String.new("minusculas").upcase

puts "#{cadena1}"
puts "#{cadena2}"

tenemos como salida:

$ ruby cadenas03.rb
mayusculas
MINUSCULAS

Los metodos de la clase String downcase y upcase regresan una copia de la cadena en minúsculas y mayúsculas respectivamente. El método new crea una nueva instancia de la cadena que se le pasa como argumento.

El siguiente código (que sera guardado en un archivo llamado "cadenas04.rb") muestra el uso de algunos de los métodos de la clase String.

str1 = String.new("Mensaje 1")
str2 = String.new("Mensaje 2")
str3 = String.new("MeNSajE 2")
str4 = String.new(" Cadena con espacios ")

puts "Las cadenas son iguales? #{str1 == str2}"
puts "Las cadenas son diferentes? #{str1 != str2}"
puts "Que pasa si multiplico la cadena? #{str1 * 2}"
puts "Comparacion de cadenas case-sensitive #{str2.eql?(str3)}"
puts "Comparacion de cadenas case-insensitive #{str2.casecmp(str3)}"
puts "Mi cadena esta vacia? #{str2.empty?}"
puts "Donde esta el '2' en mi cadena? #{str2.index("2")}"
puts "Si le quito los espacios a la derecha? [#{str4.rstrip}]"
puts "Si le quito los espacios a la izquierda? [#{str4.lstrip}]"
puts "Si le quito los espacios? [#{str4.strip}]"
puts "Mi cadena a la inversa! #{str2.reverse}"
puts "Substring! [#{str4.slice(4..22)}]"
puts "Substring! [#{str4.slice(4..-5)}]"

entonces al correr el código se tiene como salida:

$ ruby cadenas04.rb
Las cadenas son iguales? false
Las cadenas son diferentes? true
Que pasa si multiplico la cadena? Mensaje 1Mensaje 1
Comparacion de cadenas case-sensitive false
Comparacion de cadenas case-insensitive 0
Mi cadena esta vacia? false
Donde esta el '2' en mi cadena? 8
Si le quito los espacios a la derecha? [ Cadena con espacios]
Si le quito los espacios a la izquierda? [Cadena con espacios ]
Si le quito los espacios? [Cadena con espacios]
Mi cadena a la inversa! 2 ejasneM
Substring! [Cadena con espacios]
Substring! [Cadena con espacios]

¿Notan como el uso del un numero negativo en el método slice de la clase String cuenta los indices de cada carácter en la cadena de derecha a izquierda?.

Como mencione, el código anterior solo muestra algunos de los métodos de String así que si se requiere de hacer algo con una cadena primero hay que verificar si existe algún método ya en la clase String que lo haga.

Friday, February 5, 2010

Ruby Básico (IRb, Comentarios, Enteros y Cadenas)

Dicen que "Ruby es el mejor amigo del programador" y la verdad no entendía porque hasta que empece a usar un poco Ruby primero para aprender y luego para hacer pequeños programas que utilizo como herramientas en mi labor diaria.

Estos pequeños programas van desde analizadores de logs hasta programas que me ayudar a generar paquetes de instalación de una forma personalizada.

Ruby es un lenguaje orientado a objetos y fue creado en 1993 por Yukihiro Matsumoto. Es muy popular en estos tiempos y existe muchas librerías de apoyo que van desde acceder a base de datos hasta manejar XML.

Ruby viene con una consola interactiva llamada IRb (Interactive Ruby) que sirve de mucho para poder experimentar con el lenguaje. Para correr la consola interactiva simplemente hay que teclear el comando "irb".


irb(main):006:0* mensaje = "Hola Mundo!"
=> "Hola Mundo!"
irb(main):007:0> puts mensaje
Hola Mundo!
=> nil
irb(main):008:0> print mensaje
Hola Mundo!=> nil
irb(main):009:0>


Los identificadores en Ruby son aquellos nombres que les damos a las variables, nombres de métodos, clases, constantes, etc. Estos nombres si hacen distinción entre mayúsculas y minúsculas y pueden ser creados usando caracteres alfanuméricos y el guion bajo '_'.

Como todo lenguaje Ruby tiene palabras reservadas que no pueden ser usadas al nombrar nuestras variables, métodos, clases, etc., no listare las palabras reservadas aquí pero las pueden encontrar en cualquier sitio en internet.

Hay dos tipos de comentarios en Ruby: para comentar una sola linea y para comentar muchas lineas.


# Este es un comentario de una sola linea

=begin
Este es un comentario
para muchas
lineas
=end


Hay 5 tipos de variables en Ruby: globales, de instancia, locales, de clase y constantes. En este post hablare de las globales y constantes, en futuros posts donde les hable de como usar Ruby con clases y objetos ya hablaremos de los otros tipos de variables.

Las variables globales en Ruby se declaran colocando el símbolo $ al inicio del nombre de la variable


$variable_global = 3.1416


Y las constantes simplemente se declaran usando puras mayúsculas en el nombre, y cualquier intento de modificar una constante resulta en un error.


PI = 3.1416
RUTA_DESTION=/tmp/



irb(main):010:0> CONSTANTE = 3.1416
=> 3.1416
irb(main):011:0> CONSTANTE = 2
(irb):11: warning: already initialized constant CONSTANTE
=> 2
irb(main):012:0>


Los enteros en Ruby se pueden escribir con un signo al inicio y también se les puede indicar el tipo de base que se desee para el numero: 0b para binarios, 0x para hexadecimal y 0 para octal.


irb(main):012:0> numero1 = 12354
=> 12354
irb(main):013:0> numero2 = 1
=> 1
irb(main):014:0> numero1 + numero2
=> 12355
irb(main):015:0> numero3 = 0b0010
=> 2
irb(main):016:0> numero2 + numero3
=> 3
irb(main):017:0> numero4 = 0x0F
=> 15
irb(main):018:0> numero3 + numero4
=> 17
irb(main):019:0> numero5 = 1_000_000
=> 1000000
irb(main):020:0>


Como notaran el guion bajo '_' es ignorado al declaran enteros.

Ruby maneja dos clases para los enteros: Fixnum y Bignum. La diferencia entre estas clases es que Fixnum esta hecho para manejar enteros en el rango de -230 to 230-1 or -262 to 262-1 (depende de la computadora donde este corriendo Ruby) y el Bignum se usa para cuando los rangos son mayores a los anteriores.

Para los números flotantes Ruby usa la clase: Float. Los números flotantes puede ser declarados usando notación científica.


irb(main):022:0* flotante = 1.2345
=> 1.2345
irb(main):023:0> flotante2 = 1.34e4
=> 13400.0
irb(main):024:0>


La clase de Ruby String es la que maneja las cadenas. Las cadenas puede ser declaradas usando comillas simples o comillas dobles.


irb(main):026:0* cadena = 'Esta es una cadena'
=> "Esta es una cadena"
irb(main):027:0> cadena2 = "Esta es otra cadena pero con comillas dobles"
=> "Esta es otra cadena pero con comillas dobles"
irb(main):028:0>

Sunday, January 31, 2010

Qi4j y Programación Orientada a Compuestos

Hace unas semanas un amigo me recomendó esta pagina de un nuevo framework Qi4j que se centra en el desarrollo del modelo de dominio y usa conceptos de AOP, DI (Dependency Injection) y DDD (Domain Driven Design).

El framework es una implementación de un nuevo paradigma de programación que es la Programación Orientada a Compuestos (Composites) (COP - por sus siglas en Ingles) usando Java 5 puro.

Los principios de la COP son:
- El comportamiento de un objeto depende del contexto.
- El desacoplamiento es una virtud.
- Las reglas de negocio importan mas.
- Las Clases han muerto, larga vida a las Interfaces.

Ellos argumentan que la Programación Orientada a Objetos (OOP) tiene ciertos problemas para realmente modelar el ciclo de vida de los objetos. Por ejemplo, un objeto no siempre tiene los mismos atributos y estos pueden variar (tener menos o mas atributos) en el tiempo, incluso el comportamiento de un objeto no es el mismo siempre ya que puede variar según el contexto donde se encuentre ejecutando.

Este tipo de cosas ha sido muy difícil implementarlas usando OOP y aunque se pueden implementar debería ser mas "natural".

A todos nos ha pasado que empezamos un sistema haciendo el análisis de los conceptos y reglas de negocio y buscamos hacer un diseño que represente y cumpla con todos esos requisitos lo mejor posible. Pero cuando llega la hora de codificar terminamos enfocando mas nuestros esfuerzos en la infraestructura y terminamos teniendo un modelo de dominio anémico (Martin Fowler explica esto aquí).

La Programación Orientada a Compuestos esta muy influenciada por el libro de Erick Evans (Domain Driven Design) donde propone conceptos y patrones de diseño para diseñar sistemas mas enfocados al modelo de dominio.

En la COP existen dos niveles de composición:

- Un nivel de composición para ensamblar objetos compuestos a partir de piezas mas pequeñas llamadas Fragmentos.
- Y un segundo nivel de composición para ensamblar Módulos a partir de compuestos (objetos del primer nivel de composición), Capas a partir de Módulos y terminar con una Aplicación compuesta de Capas.

Los Fragmentos a partir de los cuales se puede formar un objeto compuesto son:
- Mixins. Representan el estado de un objeto compuesto.
- Restricciones (Constraints). Reglas de validación tanto de entrada como de salida para los argumentos.
- Intereses (Concerns). Interceptan llamadas de métodos. Su uso se aplica generalmente a comportamientos que aplican a todo el sistema (cross-cutting behaviours).
- Efectos Secundarios (Side Effects). Se ejecutan después de la ejecución de un método pero no influyen en los datos de salida que entregan los métodos.

En fin, el framework Qi4j se ve muy interesante y vale la pena echarle un ojo para ver la forma en que trabaja.

Links de interés:

- Resumen del libro de Erick Evans DDD. http://www.infoq.com/minibooks/domain-driven-design-quickly

Sunday, January 10, 2010

Primeros Pasos con Spring Roo en Spring Source Tool Suite

Este ejemplo de primeros pasos esta hecho con el SpringSource Tool Suite, sin embargo, es posible hacerlo también desde la consola de Roo.

Vamos a crear una pequeña aplicacion de libreta de direcciones para este ejemplo.

Lo primero es crear el proyecto del tipo "Roo Project" con el nombre "PrimerosPasos" (el nombre puede ser de su eleccion solo recuerden que con ese nombre tendrá el contexto web).


Esto crea un proyecto con la estructura:



Para aquellos que usan maven se darán inmediatamente cuenta que es una estructura de un proyecto maven.

Para crear/configurar las cosas en Spring Roo se corren comandos desde la consola Roo, la cual desde el SpringSource Tool Suite se puede ejecutar con click derecho sobre el proyecto luego la opcion Spring Tools -> Open Roo Shell.

Ya en la vista de la consola Roo existe un campo de texto donde tecleamos los comandos que pueden ir creando el código de la aplicación. Si teclean "hint" el mismo Roo les dará pistas sobre que comandos teclear según lo ya creado dentro del proyecto.

Lo primero que hay que configurar el proveedor y la base de datos de la aplicación, y de hecho este es el primer punto que pide Roo crear si teclean "hint".


persistence setup --database MYSQL --provider HIBERNATE


Esto imprime como salida en la consola de roo:


roo> persistence setup --database MYSQL --provider HIBERNATE
Created SRC_MAIN_RESOURCES/META-INF/persistence.xml
Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties
please enter your database details in src/main/resources/database.properties
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml


Dentro de los archivos que fueron creados esta el "database.properties" en el cual hay que configurar los parámetros para conectarnos a nuestra base de datos en mysql.

Ya configurados los parametros para conectarnos a la base de datos es necesario empezar a crear las entidades de nuestra aplicacion. Dado que nuestra aplicacion sera una libreta de direcciones empezaremos con la entidad "Persona".

Una vez mas podemos teclear "hint" en la consola de roo para que nos ayude.

Para crear una entidad hay que teclear el comando "entity" el cual recibe un argumento obligatorio y es el nombre de la clase de la entidad que vamos a crear.


entity --class com.jabaddon.roo.libretadirecciones.dominio.Persona


Sin embargo el comando "entity" puede recibir mas argumentos los cuales nos puede mostrar la consola de roo si tecleamos "--" y luego CRTL+SPACE.

Una vez ejecutado el comando "entity" esta es la salida en la consola de roo:


roo> entity --class com.jabaddon.roo.libretadirecciones.dominio.Persona
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona.java
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_Entity.aj
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_ToString.aj
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_Configurable.aj


Notaran que la única clase java creada fue "Persona.java" y que si abren el código solo verán lo siguiente:


@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Persona {
}


Los otros archivos "*.aj" son Aspectos agregados a la clase Persona.

En este punto si volvemos a teclear "hint" en la consola de roo nos dará consejos para crearle atributos a nuestra clase.

Agregaremos el atributo nombre a nuestra clase Persona ejecutando el siguientes comando en la consola:


field string --fieldName nombre



roo> field string --fieldName nombre
Managed SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona.java
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_JavaBean.aj
Managed SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/dominio/Persona_Roo_ToString.aj


Ok, en este punto del ejemplo me gustaría recalcar algo y para lograrlo primero hay que crear una clase de prueba de la clase Persona con el siguiente código:


package com.jabaddon.roo.libretadirecciones.dominio;

import org.apache.log4j.Logger;
import org.junit.Test;

public class PersonaTest {

private static final Logger LOGGER = Logger.getLogger(PersonaTest.class);

@Test
public void imprimirNombrePersona() {
Persona persona = new Persona();
persona.setNombre("Jimmy");
LOGGER.debug("El nombre es = " + persona.getNombre());
}
}


Lo siguiente es crear un archivo "log4j.propeties" en src/test/resources y configurarlo para que imprima el nivel debug de la siguiente forma: (si quieren pueden copiar el log4j.properties que se genero en src/main/resources/META-INF/spring/log4j.properties, pero hay que cambiarle el nivel a debug).


log4j.rootLogger=debug, consola

log4j.appender.consola=org.apache.log4j.ConsoleAppender
log4j.appender.consola.layout=org.apache.log4j.PatternLayout
log4j.appender.consola.layout.ConversionPattern=%d [%t] %-5p %c - %m%n


Una vez creada la prueba unitaria y el archivo log4j.properties, hay que ejecutar las pruebas del proyecto dando click derecho sobre el archivo pom.xml y ejecutar la opcion "Run As -> Maven Test". Con esto ejecutaremos las pruebas unitarias del proyecto y la salida de ejecutar esta acción es la siguiente (o pueden ejectuar las pruebas corriendo el comando "mvn test" en una consola normal):


...
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.jabaddon.roo.libretadirecciones.dominio.PersonaTest
2010-01-10 21:52:35,441 [main] DEBUG org.springframework.beans.factory.wiring.BeanConfigurerSupport - BeanFactory has not been set on BeanConfigurerSupport: Make sure this configurer runs in a Spring container. Unable to configure bean of type [com.jabaddon.roo.libretadirecciones.dominio.Persona]. Proceeding without injection.
2010-01-10 21:52:35,444 [main] DEBUG com.jabaddon.roo.libretadirecciones.dominio.PersonaTest - El nombre es = Jimmy
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.931 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
...


Ok, todo fue bien el nombre Jimmy se imprimió bien. Ahora miremos de nuevo el código de la clase Persona.java:


package com.jabaddon.roo.libretadirecciones.dominio;

import javax.persistence.Entity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.roo.addon.entity.RooEntity;

@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Persona {

private String nombre;
}


Alguien puede explicarme porque al crear la prueba unitaria la clase Persona contenía los métodos setNombre y getNombre si el código java no lo tiene?!??!?!?!. La respuesta son dos palabras: Aspectos y Addons.

Recuerdan que al crear la entidad Persona Spring Roo creo unos archivos .aj? estos son los Aspectos. Vía aspectos y anotaciones Spring Roo agrega funcionalidad a las clases. Estos aspectos agregados por anotaciones a las clases son los llamados "Addons".

Los "Addons" son una característica que propone Spring Roo y es muy interesante ya que así podemos agregar funcionalidad a las clases de acuerdo a las necesidades y circunstancias. En algun post siguiente en el blog veremos mas a detalle acerca de los aspectos y addons de Spring Roo incluso para crear uno propio, por ahora continuemos con el ejemplo.

Ok, nuestra clase Persona ya tienen un atributo y es el nombre. Ahora vamos a agregarle otros atributos como: apellido y numero telefonico con los comandos siguientes:

                            
field string --fieldName apellido
field string --fieldName numeroTelefonico


Ahora crearemos la parte web de la aplicación creando los controllers. Para que roo nos ayude hay que teclean "hint controllers" en la consola de roo.

Para crear un controller que contenga las operaciones clasicas de altas, bajas y modificaciones (CRUD) ejecutamos el comando:


controller scaffold --class com.jabaddon.roo.libretadirecciones.web.PersonaController --entity com.jabaddon.roo.libretadirecciones.dominio.Persona


La salida de la ejecucion de este comando es la siguiente:


roo> controller scaffold --class com.jabaddon.roo.libretadirecciones.web.PersonaController --entity com.jabaddon.roo.libretadirecciones.dominio.Persona
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/web
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/web/PersonaController.java
Created SRC_MAIN_WEBAPP/WEB-INF/spring
Created SRC_MAIN_WEBAPP/WEB-INF/spring/webmvc-config.xml
Created SRC_MAIN_JAVA/com/jabaddon/roo/libretadirecciones/web/PersonaController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/images
Created SRC_MAIN_WEBAPP/images/nl.png
Created SRC_MAIN_WEBAPP/images/update.png
Created SRC_MAIN_WEBAPP/images/es.png
Created SRC_MAIN_WEBAPP/images/delete.png
Created SRC_MAIN_WEBAPP/images/add.png
Created SRC_MAIN_WEBAPP/images/resultset_previous.png
Created SRC_MAIN_WEBAPP/images/resultset_next.png
Created SRC_MAIN_WEBAPP/images/favicon.ico
Created SRC_MAIN_WEBAPP/images/banner-graphic.png
Created SRC_MAIN_WEBAPP/images/resultset_last.png
Created SRC_MAIN_WEBAPP/images/gb.png
Created SRC_MAIN_WEBAPP/images/springsource-logo.png
Created SRC_MAIN_WEBAPP/images/it.png
Created SRC_MAIN_WEBAPP/images/show.png
Created SRC_MAIN_WEBAPP/images/sv.png
Created SRC_MAIN_WEBAPP/images/list.png
Created SRC_MAIN_WEBAPP/images/resultset_first.png
Created SRC_MAIN_WEBAPP/images/de.png
Created SRC_MAIN_WEBAPP/styles
Created SRC_MAIN_WEBAPP/styles/standard.css
Created SRC_MAIN_WEBAPP/styles/alt.css
Created SRC_MAIN_WEBAPP/WEB-INF/classes
Created SRC_MAIN_WEBAPP/WEB-INF/classes/standard.properties
Created SRC_MAIN_WEBAPP/WEB-INF/classes/alt.properties
Created SRC_MAIN_WEBAPP/WEB-INF/layouts
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/default.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/layouts.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views
Created SRC_MAIN_WEBAPP/WEB-INF/views/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views/resourceNotFound.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/index.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/uncaughtException.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/dataAccessFailure.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/controller-index.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/tags
Created SRC_MAIN_WEBAPP/WEB-INF/tags/language.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/theme.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/pagination.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/i18n
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_sv.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_nl.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_it.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_es.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_de.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/list.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/show.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/create.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/update.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/application.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/persona/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/urlrewrite.xml
Created SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml
Managed ROOT/pom.xml


Como verán, Spring Roo ha creado todos los archivos necesarios para tener una aplicación básica de altas, bajas y modificaciones sobre nuestra entidad Persona.

Para correr la aplicación bien podemos dar click derecho sobre el proyecto de Spring Source Tool Suite y darle "Run As -> Run on Server" o en una consola normal "mvn jetty:run".

Una vez que ha levantado el servidor vayan a la direccion http://localhost:8080/PrimerosPasos y listo!

Notaran que en la base de datos ya existe una tabla llamada "persona" con los atributos iguales a los que tiene nuestra entidad Persona.

NOTA: Yo tuve problemas ejecutando este ejemplo con Java 1.5 en una Mac pero todo corrió bien con Java 1.6.






Saturday, November 14, 2009

Spring 3: Spring Expression Language (SpEL)


En la versión 3 de Spring podemos encontrar una nueva característica y es el SpEL (Spring Expression Language). Es una nueva forma para inyectar valores a las propiedades de los beans.

Por ejemplo, todo hijo hereda el apellido del padre:














Al ejecutar el siguiente codigo java:


ApplicationContext appContext =
new ClassPathXmlApplicationContext("classpath:spring3-config.xml");
Persona padre = (Persona) appContext.getBean("padre");
Persona hijo = (Persona) appContext.getBean("hijo");
System.out.println("Apellido del padre = " + padre.getApellido());
System.out.println("Apellido del hijo = " + hijo.getApellido());

El resultado es el siguiente:


Apellido del padre = Lara
Apellido del hijo = Lara

Spring evalúa la expresión "#{padre.apellido}" y la traduce a una llamada el método "getApellido" del bean "padre". El resultado es inyectado a la propiedad "apellido" del bean "hijo". Esta nueva característica del Spring 3 es muy interesante ya que no solo permite acceder a propiedades de otros beans, también llamar metodos de otros beans, obtener valores de propiedades del sistema y hasta llamar métodos estáticos.

Veamos algunos ejemplos:


  • Acceder a propiedades del sistema: #{ systemProperties['valor.systema]}.
  • Llamar métodos estáticos: #{ T(java.lang.Math).random() }.
  • Acceder a elementos de un arreglo: #{ arreglo[1] }.
  • Acceder a elementos de un mapa: #{ mapa['key'] }.
  • Llamar a métodos (con todo y argumentos): #{ 'cadena'.substring(2, 3) }.
  • Operaciones aritméticas: #{ (2 + 2) * 5 }
  • Crear objetos: #{ new String('Hola') }.

En fin, hay muchas otras cosas mas que se pueden hacer con esta nueva característica que viene con Spring 3

Pueden leer mas en la documentación de referencia aquí.

Saturday, October 24, 2009

Instalacion de Jackbe Presto en Windows Vista

Hace poco quise instalar Jackbe Presto en Windows Vista y me tope con un problema ya que al correr el bat que hace la instalación 'setup.bat' mandaba un error (no recuerdo el error :P).

El contenido del archivo setup.bat tiene lo siguiente:
cscript //NoLogo setup_win.js
Al agregarle la siguiente opción la instalación funciono sin problemas:
cscript //NoLogo setup_win.js //E:javascript


Monday, September 14, 2009

Muse - The Resistance

Mañana es el día (15-Septiembre-2009) en que la banda inglesa de rock alternativo Muse saca a la venta (por lo menos en iTunes Music Store) su nuevo disco titulado 'The Resistance'.

El disco esta compuesto por los siguientes tracks:
  1. Uprising. Gran rola y el primer single oficial del disco.
  2. Resistance.
  3. Undisclosed Desires. Muy buenos arreglos y coros.
  4. United States of Eurasia. Buenos coros, me recuerdan a Queen, y termina con un gran toque de musica clasica (Nocturna Op. 9 No. 2 de Chopin).
  5. Guiding Light. Un poco melosa y con falta de cambio de ritmos.
  6. Unnatural Selection. Otra buena rola, buena guitarra rítmica muy de Muse.
  7. MK Ultra.
  8. I Belong to You. Muy buen ritmo y termina excelentemente con un fragmento de la opera "Samson et Dalila" de Camille Saint-Saêns.
  9. Exogenesis: Symphony, Pt. 1 (Overture).
  10. Exogenesis: Symphony, Pt. 2 (Cross-pollination).
  11. Exogenesis: Symphony, Pt. 3 (Redemption). Gran piano.
En general es un disco no tan roquero como los anteriores de Muse pero que da una muestra del talento que tienen los Muse para componer música sobre todo de Mattew Bellamy. Muy recomendable.