viernes, febrero 29, 2008

Spring Integration


El nuevo miembro de la famila, Spring Integration, nos permite utilizar los famosos patrones de integración dentro de nuestra aplicación, sin alejarnos del modelo de programación de Spring.

Con una configuración tan sencilla como esta se logra leer ficheros de un directorio y procesarlos:


<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">

<message-bus/>
<channel id="fileInputChannel" />
<annotation-driven/>

<context:component-scan base-package="com.habuma.si.example" />

<file-source directory="/Users/wallsc/sucker"
poll-period="1000" channel="fileInputChannel"/>
</beans:beans>
----
package com.habuma.si.example;
import org.apache.log4j.Logger;
import org.springframework.integration.annotation.Handler;
import org.springframework.integration.annotation.MessageEndpoint;

@MessageEndpoint(input = "fileInputChannel")
public class FileSucker {
private static final Logger LOGGER = Logger.getLogger(FileSucker.class);

@Handler
public void suckAFile(String fileContents) {
LOGGER.debug(fileContents);
}
}


Pero todo no acaba allí. Los mensajes de entrada (los ficheros en el ejemplo anterior) se pueden enrutar hacia otras salidas: otros ficheros, colas JMS, servicios web o cualquier sistema del que dispongamos (o desarrollemos) adaptadores.
Del mismo modo, los mensajes de entrada pueden provenir de diversas fuentes.

El ejemplo anterior está amablemente copiado de Spring Integration: Meet the channel adapters. En el mismo blog de Craig Walls encontraremos mas información y ejemplos de utilización.

miércoles, agosto 01, 2007

Atlassian compra Cenqua


Quién sabe que puede surgir de esta unión: Atlassian compra Cenqua
Con aplicaciones como FishEye, Crucible y Clover, la gama de productos de Atlassian es impresionante.

jueves, julio 12, 2007

Usando Confluence wiki para documentar en forma agil


Gestionar los requisitos de un proyecto no es tarea fácil. ¿Pero no es fácil por la complejidad de obtenerlos o la de documentarlos? Son dos caras de una misma moneda y en definitiva la buena gestión de ambas tareas hará que el proyecto salga adelante con éxito o no.
Para obtenerlos hay muchas metodologías, tantas como gustos. Quizás RUP sea la mas conocida en proyectos grandes y las variantes de Agile para proyectos mas pequeños. Pero cualquiera sea la metodología elegida del proyecto, y por lo tanto de como gestionar los requisitos, las herramientas para documentar son un poco mas que penosas.
El rey de las herramientas de requisitos, RequisitePro es una arcaica mezcla de MS Word y aplicación propietaria, junto con una herramienta de reportes (SODA) en MS Word. Nada ágil por cierto. Quizás con el nuevo desarrollo Jazz todo mejore para Rational, pero para eso faltan no menos de 2 años. ¿Que nos queda entonces para documentar los requisitos? Quizás Enterprise Arquitect sea bueno para esto, pero sigue siendo un cliente pesado.
¡Bienvenidos a la web entonces! Quizás el actual sistema de documentación distribuído mas popular de estos días sea la wikipedia, por que no documentar entonces un proyecto en un wiki? El primer pensamiento en contra es por lo "desestructurada" que suele ser la información guardada en un wiki, pero alguas herramientas nos permiten agregar control sobre los formularios de entrada y edición de datos. XWiki es un ejemplo de ello, pero el ejemplo que nos ocupa hoy está desarrollado con Confluence y una serie de plugins disponibles para esta plataforma.

Paso 1. Crear plantilla de doucmento Cliente.
primero crearemos una plantilla para contener los datos de los clientes, de los cuales, por simplicidad, solo guardaremos el nombre. Para ello deberemos ir a "Browse Space/Advanced/Templates/Add New Space Template" y agregar el siguiente código bajo el nombre "Client":



| Nombre: | {page-info:title} |

{report-table}
{content-reporter:spaces=@self|type=page|scope=@self > children}
{text-sort:content:title|order=ascending}
{text-filter:data:FormName|include=Project}
{content-reporter}
{report-column:title=Nombre}{report-info:content:title|link=true}{report-column}
{report-column:title=Project Leader}{report-info:data:ProjectLeader|link=true}{report-column}
{report-column:title=Start Date}{report-info:data:StartDate|link=true}{report-column}
{report-column:title=End Date}{report-info:data:EndDate|link=true}{report-column}
{report-column:title=Status}{report-info:data:Status|link=true}{report-column}
{report-table}
(+) {add-page:template=Project|live=true}Agregar proyecto{add-page}

{set-data:FormName|hidden=true}Client{set-data}


En esta plantilla vemos una tabla con el título del documento, un reporte de proyectos y un link para agregar un nuevo proyecto. Por último el tag {set-data}, del plugin Scaffolding, nos permite identificar este documento con la propiedad "FormName=Client", lo que nos facilitará los reportes de clientes que querramos hacer.

Paso 2. Crear documento raíz.
Luego necesitamos una página en algún lugar de nuestro wiki, que contenga el listado de clientes. Para el listado utilizaremos el plugin Reporting:



{report-table}
{content-reporter:spaces=@self|type=page|scope=Clients > children}
{text-sort:content:title|order=ascending}
{content-reporter}
{report-column:title=Nombre}{report-info:content:title|link=true}{report-column}
{report-table}

(+) {add-page:template=Client|live=true}Agregar cliente{add-page}


Al final de la página se puede ver el tag {add-page} para crear documentos de tipo cliente mediante una plantilla previamente creada. Luego de salvar la página con el título "Clients", podemos agregar un cliente:

Paso 3. Agregar un cliente.


Lo que nos presentará un documento a crear en base a la plantilla "Client":



Que luego de salvarlo tendrá la siguiente apariencia:



Paso 4. Crear plantilla de proyecto
Análogamente, para crear un proyecto deberemos crear primero una plantilla, con título "Project":

| Nombre: | {page-info:title} |
| Cliente: | {report-link:content:parent > content:url}{report-info:key=content:parent}{report-link} |
| Codigo JIRA | {text-data:JiraCode|content=text|width=100px}{text-data} |
| Project Leader: | {list-data:ProjectLeader|required=true}
{user-options:groups=confluence-users}
{list-data} |
| Team Members: | {list-data:TeamMembers|type=check|multiple=true}
{user-options:confluence-users}
{list-data} |
| Start Date: | {date-data:StartDate|format=dd-MMM-yyyy}today{date-data} |
| End Date: | {date-data:EndDate|format=dd-MMM-yyyy}{date-data} |
| Status: | {list-data:Status}
{list-option}Unstarted{list-option}
{list-option}In progress{list-option}
{list-option}Awaiting approval{list-option}
{list-option}Completed{list-option}
{list-data} |

[Requerimientos|Requirements]
[Casos de Uso|UseCases]
[Iteraciones|Iterations]
[Actas de Reunión|MeetingMinutes]
[Informes de estado de proyecto|StatusReports]

{set-data:FormName|hidden=true}Project{set-data}


Esta plantilla tiene los datos propios del proyecto, incluyendo su project leader, participantes, fecha de inicio y fin y estado. Al final también hay links a los documentos que harán de padres de los requisitos, las iteraciones y todos los demás documentos que nuestra metodología requiera.

Paso 5. Crear un proyecto.
Luego de creada la plantilla de proyecto podremos crear proyectos, mediante el link en el formulario correspondiente al cliente:



Lo cual nos preguntará el título del proyecto, y luego los datos que especifica la plantilla correspondiente:



Que una vez salvada tendrá la siguiente apariencia:



Paso 6. El listado de requierimientos.
Queda como ejercicio resolver que cada proyecto contenga su propia lista de requerimientos, ya que como ven en la plantilla de proyecto el link a los requerimientos es a una página genérica, llamada "Requirements": [Requerimientos|Requirements]. La mejor posibilidad es utilizar un "space" para cada proyecto.
Pero vamos con el diseño de la plantilla de la citada página padre "Requirements":

h3.Requerimientos Funcionales

{report-table}
{content-reporter:type=page|scope=@self > children}
{text-sort:data:Date|order=ascending}
{text-filter:data:FormName|include=Requirement}
{text-filter:data:Type|include=Funcional}
{content-reporter}
{report-column:title=Título}{report-info:content:title|link=true}{report-column}
{report-column:title=Estado}{report-info:data:State}{report-column}
{report-column:title=Prioridad}{report-info:data:Priority}{report-column}
{report-column:title=Dueño}{report-info:data:Owner}{report-column}
{report-column:title=Creado}{report-info:content:creation date}{report-column}
{report-column:title=Creado por}{report-info:content:creator}{report-column}
{report-table}

h3.Requerimientos No Funcionales
{report-table}
{content-reporter:type=page|scope=@self > children}
{text-sort:data:Date|order=ascending}
{text-filter:data:FormName|include=Requirement}
{text-filter:data:Type|include=No Funcional}
{content-reporter}
{report-column:title=Título}{report-info:content:title|link=true}{report-column}
{report-column:title=Estado}{report-info:data:State}{report-column}
{report-column:title=Prioridad}{report-info:data:Priority}{report-column}
{report-column:title=Dueño}{report-info:data:Owner}{report-column}
{report-column:title=Creado}{report-info:content:creation date}{report-column}
{report-column:title=Creado por}{report-info:content:creator}{report-column}
{report-table}

(+) {add-page:template=Requirement|live=true}Agregar requerimiento{add-page}


Se ha dividido la página en requerimientos funcionales y no funcionales, pero eso es a gusto del consumidor. Lo que hay que saber es que esta página lista los documentos de tipo "Requirement", creados a su vez con la plantilla "Requirement":

Paso 7. El formulario de requirimientos:

{section}
{column:width=300px}

*Detalles*
{table:width=300px}

{table-row}
{table-cell}
Tipo:
{table-cell}
{table-cell}
{list-data:Type|required=true}
{list-option}Funcional{list-option}
{list-option}No Funcional{list-option}
{list-data}
{table-cell}
{table-row}
{table-row}
{table-cell:width=100px}
Prioridad:
{table-cell}
{table-cell:width=200px}
{list-data:Priority}
{list-option}Sin asignar{list-option}
{list-option}Alta{list-option}
{list-option}Media{list-option}
{list-option}Baja{list-option}
{list-data}
{table-cell}
{table-row}
{table-row}
{table-cell:width=100px}
Estado:
{table-cell}
{table-cell:width=200px}
{list-data:State}
{list-option}Propuesto{list-option}
{list-option}Rechazado{list-option}
{list-option}Aprobado{list-option}
{list-option}Diferido{list-option}
{list-data}
{table-cell}
{table-row}
{table-row}
{table-cell}
Dueño:
{table-cell}
{table-cell}
{list-data:Owner|required=true}{user-options:groups=confluence-users}{list-data}
{table-cell}
{table-row}

{table}

{column}
{column:width=90%}
*Descripción*
{text-data:Description|type=area|content=wiki|width=100%|height=500px}
{column}

{section}

{set-data:FormName|hidden=true}Requirement{set-data}


El resultado es un formulario:



Y un listado de todos requerimientos:



Finalmente, queda por decir que el plugin para Graphviz nos permite hacer diagramas sin salir del wiki (ver UML class diagrams in Confluence using Graphviz and DOT), lo que aumenta varios grados la agilidad de la documentación y permite gráficos como este:

martes, julio 03, 2007

Gestión de excepciones en Java


Acerca de Exception-Handling Antipatterns
Tim McCune nos propone en un "antiguo" artículo (del 2006) varias formas no hacer bien las cosas cuando de excepciones se trata. Sin copiar todo el artículo, aquí van algunos de mis antipatrones preferidos:

Log and Throw
(Duplicación de entradas de error en los logs.)
catch (NoSuchMethodException e) {
e.printStackTrace();
throw new MyServiceException("Blah", e);
}


Throwing Exception
(Poco explícito)
public void foo() throws Exception


Catch and Ignore
(Se destruye la excepción y no se devuelve un error)
catch (NoSuchMethodException e) {
return null;
}


Destructive Wrapping
(Se destruye la excepción original)
catch (NoSuchMethodException e) {
throw new MyServiceException("Blah: " +
e.getMessage());
}

martes, mayo 08, 2007

Semmle, al fin una idea original


Semmle es una herramienta para hacer consultas de nuestro código como si de una base de datos se tratara. La siguiente consulta
from Class c 
where c.declaresMethod("equals")
and not(c.declaresMethod("hashCode"))
select c.getPackage(),c
devuelve el resultado:
Photo Sharing and Video Hosting at Photobucket
para el working space "framework" de mi workspace de eclipse (Todas las clases que definen un método equals, pero no hashCode).
Basado en ".QL", un lenguaje de queries orientado a objetos, permite hacer nuestras propias clases para ampliar las funcionalidades de búsqueda.
Con una biblioteca de métricas de código tales como Lack of Cohesion of Methods y Afferent Coupling/Efferent Coupling, solo le falta una tarea ant para agregar esta herramienta al servidor de integración continua.
Al fin una idea original!

martes, diciembre 05, 2006

Nuevo tipo de configuración basado en anotaciones para Spring


Como ya saben, hay una nueva forma de configurar Spring. XML es el tipo de configuración mas utilizada para Spring, pero con este nuevo tipo, basado en anotaciones, podemos cambiar algo del "infierno xml". Dejaremos en paz la configuración mediante Groovy, al menos por ahora.
Los desarrolladores por ejemplo, pueden utilizar esto para alguna forma de configuración interna, dejando la configuración xml para infraestructura.
Pueden mirar en la web del proyecto las novedades, pero imaginen un xml como este:

<beans>
<bean id="myCompany" class="package.Company"/>
<bean id="employee" class="Employee" scope="prototype">
<property name="company" ref="myCompany"/>
</bean>
</beans>

podría hacerse así:

@Configuration
public class AppConfiguration {
@Bean
public Company myCompany() {
return new Company();
}

@Bean(scope = Scope.PROTOTYPE)
public Employee employee() {
Employee employee = new Employee();
employee.setCompany(myCompany());
return employee;
}
}

iniciando mas tarde un ApplicationContext con un bean ConfigurationPostProcessor, pudiendo incluso mezclar beans de ambos métodos:

<beans>
<bean class="package.AppConfiguration"/>
<bean class="org.springframework.beans.factory.java.ConfigurationPostProcessor"/>
<bean class="package.OtherClass">
<property name="employee" ref="employee"/>
</bean>
</beans>

Rod también nos muestra un nuevo application context (por Costin Leau):

ApplicationContext oneConfig =
new AnnotationApplicationContext(SimpleConfiguration.class.getName());

y

ApplicationContext aBunchOfConfigs =
new AnnotationApplicationContext("**/configuration/*Configuration.class");

Dejaremos a nuestra imaginación una forma de configuración mas dinámica todavía, mezclando loops y if/thens en el bean de configuración!
El código puede ser encontrado aquí. Que lo disfruten!

jueves, noviembre 30, 2006

Nuevo namespace en Spring 2.0


Una forma mas bien rara de configurar los beans de Spring parece ser la que Rod Johnson nos recuerda en una entrada de su blog.
Básicamente se puede utilizar un nuevo namespace "p" para utilizar los atributos de un XML como propiedades de los beans.
Lo que normalmente haríamos así:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="empleado" class="package.Empleado">
<property name="empresa" ref="empresa" />
<property name="horasSemanales" value="35" />
</bean>

<bean id="empresa" class="package.Empresa" />

</beans>
Lo podríamos hacer así:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="empleado" class="package.Empleado"
p:direccion-ref="empresa"
p:horasSemanales="35" />

<bean id="empresa" class="package.Empresa" />

</beans>
Un pequeño cambio que podría hacer nuestros XMLs mas compactos, aunque no se si mas legibles.

Mas ejemplos aquí.