Archivos del sitio javahispano

Por fin un estándar con el que entretejer nuestras aplicaciones.

Posteado por Félix García Borrego en October 25th, 2009

Recientemente se ha liberado la primera versión estable de Weld, la implementación de referencia de Web Beans (Java Context and Dependenty Injection - JSR 299). Y aunque puede parecer una liberación cualquiera, en este caso estamos ante un evento realmente importante para el mundillo Java.

¿Por qué es importante esta liberación?

  • Por fin tenemos un estándar Java para la inyección, publicación, localización y búsqueda de componentes.
  • Promete una mejor orquestación entre componentes JEE, evitando los típicos problemas de interoperabilidad entre Beans Spring, componentes Seam, Wicket, JSF, EL, etc..)
  • Un remplazo estándar a las diferentes soluciones de inyección e IoC como Spring IoC, el prometedor Google Guice o el muy utilizado por nosotros Jboss Seam annotations.
  • Fin del infierno XML, definiendo un mecanismo estándar independiente de ficheros de definición XML, que junto con JSF 2.0, Servlet 3.0, JPA, Jax-ws, etc… simplifican el desarrollo JEE.

¿Consecuencias para la industria a medio plazo?

  • Es demasiado pronto para saberlo, pero…
    ¿Quizás el fin de la guerra de frameworks se inyección?…. (Google Guice vs Spring vs Jboss Seam). Consiguiendo un efecto similar al que consiguió JPA/JDO estandarizando el acceso a datos.
  • Por fin una especificación JEE con la que construir aplicaciones complejas de forma sencilla.
  • En un momento en el que muchas organizaciones están intentando estandarizar sus desarrollos, llegando incluso a restringir o imponer las tecnologías utilizadas, la preselección de implementaciones como Spring IoC o Seam carecerá de sentido. Siendo mucho más coherente la recomendación de estándares en lugar de restringir la evolución natural de la tecnología y la sana competición entre sus diferentes implementaciones.

¿Consecuencia para nosotros?

  • Ninguna…. Gracias a nuestra apuesta por Jboss Seam (desde hace ya unos años), esta especificación se encontraba ya en nuestra hoja de ruta antes incluso de que fuese un Draft, por lo que para nosotros será suficiente con una mínima adaptación al estándar.
  • La implementación es estable, la especificación muy bien pensada, y en las pruebas que hemos realizado con la beta, ha demostrado que consigue simplificar los desarrollos y hacer más comprensible el “oscuro arte de la inyección de componentes”.
  • En breve empezaremos a usarla en algún proyecto piloto (que pueda asumir el riesgo de I+D asociado), analizaremos en detalle los posibles problemas que puedan surgir, y si todo se comporta como promete a mediados de 2010 empezaremos a usar la nueva especificación en nuestros proyectos Java.

¿Defectos?

  • Llega demasiado tarde. Otros frameworks llevan, aunque de una forma mucho menos limpia,  ofreciendo algo similar desde hace ya mucho tiempo.
  • Todavía no conocemos sus defectos. Y eso es un gran problema ya que hasta que no conozcamos sus problemáticas asociadas no será seguro su uso en proyectos de envergadura.
  • Esperemos que en la huida del infierno de los ficheros de configuración XML, no nos metamos en  las tinieblas del uso de inyección e IoC para todo. Ya que una aplicación con una gran cantidad de componentes, comportamientos o atributos que dependen de la inyección hace muy complicada la comprensión de su funcionamiento.

En definitiva, todo indica que estamos antes una de las piezas clave del próximo JEE 6.

Guía de migración de proyectos Java a Maven

Posteado por Félix García Borrego en October 16th, 2009

Objetivos

El objetivo de este artículo es servir de guía básica para la migración manual de proyectos Java no Maven a entorno Maven. Pensado y dirigido especialmente a personas con poca  experiencia previa en Maven, pero con algún conocimiento previo.

Preparación del proyecto

Como es obvio, para migrar un proyecto Java a Maven, deberemos disponer de sus fuentes y todas las librerías dependientes.

Ya que vamos a realizar modificación estructurales importantes en el proyecto, conviene asegurarnos de que los fuentes no están conectados a ningún sistema de control de versiones como Subversion. Para realizar esto, podemos o bien eliminar manualmente todos los directorios .svn de nuestro proyecto o bien desde Eclipse desconectar el proyecto.

Si lo vamos a realizar de forma manual, debemos asegurarnos de eliminar todos los directorios .svn (o .CVS si el proyecto a migrar estuviese conectado a un CVS) A modo de ejemplo, en Linux podemos ejecutar el siguiente comando desde el directorio del proyecto para eliminar todas las referencias a carpetas ‘.svn’:

 

rm -rf `find . -type d -name .svn`

Será mucho más sencillo hacerlo de forma automática desde Eclipse. Para desconectar el proyecto, teniéndolo seleccionado pulsaremos sobre el botón derecho del ratón: Team -> Disconnect.

Desconectar SVN desde Eclipse

Adaptación del proyecto

Aunque la estructura por defecto Maven puede ser modificada, es muy recomendable adaptar la organización del proyecto a migrar a la estructura por defecto más generalizada:

estructura Maven

Para hacer esta modificación de la estructura hay que tener especial cuidado en algunos aspecto o convenciones Maven:

  • Los ficheros de recursos: Maven hace una distinción entre los ficheros de clases java y los ficheros de recursos .properties y XML’s y, de hecho, salvo que le digamos lo contrario, no hará ningún tratamiento de los ficheros no java que se encuentren en el directorio src/main/java. Por este motivo, tendremos que localizar todos los ficheros de recursos (properties, XML’s) que se encuentran en el directorio java y moverlos al directorio resources (src/main/resources).
  • Eliminar la carpeta classes. Es muy común que proyectos no Maven tengan en su carpeta WEB-INF/classes los compilados del proyecto. Esta situación no sólo no es recomendable sino que puede producir problemas, por lo que los eliminaremos confiando en Maven para su generación cuando sea necesario.

Definir el pom.xml del proyecto

Una vez que la estructura del proyecto se ha adaptado a la filosofía Maven, el siguiente paso es definir el pom.xml del proyecto.

Partiremos de un fichero pom.xml básico. Este fichero de origen no tiene dependencias; durante el proceso lo adaptaremos y posteriormente iremos refinando la configuración hasta alcanzar una “mavenización” completa del proyecto.

Una vez colocado el pom.xml en el raíz de la aplicación, lo modificamos indicando el nombre del proyecto y la información básica relativa a los desarrolladores, organización y repositorio de dependencias utilizado.

Antes de continuar y añadir las dependencias, podemos hacer una comprobación previa y verificar que efectivamente nuestro IDE, en este caso Eclipse, reconoce correctamente la nueva estructura del proyecto.

Para ello nos vamos a la consola y en el directorio en el que se encuentra nuestro proyecto ejecutamos la siguiente orden:

mvn clean eclipse:eclipse

(Requiere disponer de Maven correctamente instalado en la máquina).

Si la configuración del entorno es correcta, obtendremos una salida similar a esta:

 

[INFO] Scanning for projects…
[INFO] Searching repository for plugin with prefix: ‘eclipse’.
[INFO] ————————————————————————
[INFO] Building ejemplo
[INFO]    task-segment: [clean, eclipse:eclipse]
[INFO] ————————————————————————
[INFO] [clean:clean]
[INFO] Deleting file set: /home/felix/workspaces/ejemplo/target (included: [**], excluded: [])
[INFO] Preparing eclipse:eclipse
[INFO] No goals needed for project - skipping
[INFO] [eclipse:eclipse]
[INFO] Adding support for WTP version 1.5.
[INFO] Using source status cache: /home/felix/workspaces/ejemplo/target/mvn-eclipse-cache.properties
[INFO] File /home/felix/workspaces/ejemplo/.project already exists.
       Additional settings will be preserved, run mvn eclipse:clean if you want old settings to be removed.
[INFO] Wrote Eclipse project for ejemplo to /home/felix/workspaces/ejemplo.
[INFO]
       Sources for some artifacts are not available.
       Please run the same goal with the -DdownloadSources=true parameter in order to check remote repositories for sources.
       List of artifacts without a source archive:
         o javax.servlet:servlet-api:2.4
         o javax.servlet.jsp:jsp-api:2.1
         o junit:junit:3.8.1 

[INFO] ————————————————————————
[INFO] BUILD SUCCESSFUL
[INFO] ————————————————————————
[INFO] Total time: 4 seconds
[INFO] Finished at: Wed Sep 3 11:54:28 CET 2008
[INFO] Final Memory: 10M/80M
[INFO] ————————————————————————

Nos vamos a Eclipse, refrescamos el proyecto (F5 teniendo seleccionado el proyecto) y comprobamos que la nueva estructura es correcta.

Ahora que hemos comprobado que eclipse ha reconocido la nueva estructura, el siguiente paso es definir en nuestro pom.xml las dependencias. Para hacer esto eliminamos todos los .jar que se encuentran en el directorio /WEB-INF/lib y nos quedamos con una copia de ellos para posteriormente ir resolviendo las dependencias del proyecto original. Una recomendación es cortar la carpeta /WEB-INF/lib a otra ruta de nuestro disco.

Resolución de dependencias

Esta es la fase más delicada del proceso, ya que debemos asegurarnos de que todas las dependencias del proyecto quedan correctamente declaradas en el fichero pom.xml. Para realizar esta tarea es recomendadle utilizar alguna aplicación web que nos ayude a la localización de las librerías, como por ejemplo MvnRepository o MvnBrowser (recomendada), en la que podremos buscar cada uno de nuestros jar y encontrar la definición en el pom.xml que debemos utilizar.

Migración de proyectos maven con mvnBrowser

Gracias a esta web, una vez que tengamos localizada la dependencia, solo tendremos que copiar el bloque POM Dependency a nuestro fichero pom.xml.

Iremos repitiendo este proceso de forma iterativa para todos nuestras librerías .jar y añadiendo la definición de la dependencia a nuestra zona de dependencies, con ciertas salvedades y consideraciones que introducimos a continuación.

Consideraciones especiales

Hay ocasiones en las que determinar la definición de la dependencia en nuestro pom.xml puede no ser una tarea trivial. Se comentan a continuación los mecanismos de actuación recomendados según diversas casuísticas:

  • No se indica la versión de la librería en el nombre del fichero.

Por ejemplo, podemos encontrarnos una librería llamada axis-ant.jar, por lo que tendremos que averiguar previamente la versión de la librería de cara a definir la dependencia en el fichero pom.xml. Para esto suele ser muy efectivo descomprimir la librería y consultar el fichero /META-INF/MANIFEST.MF. A modo de ejemplo, en dicha librería mencionada observaríamos una linea similar a la siguiente: “Implementation-Version: 1.2.1 2243 June 14 2005″. Una vez que conozcamos la versión podremos realizar la búsqueda de la forma usual. , 1.1.1.1. Sabemos que la librería fue generada por maven, pero no conocemos su definición.

No conocemos el groupId, artifactId, etc. Podemos descomprimir el .jar y acceder al fichero pom.xml que se encuentra en /META-INF/maven para comprobar el groupId, artifactId y versión de la dependencia.

  • Dependencias especiales JEE.

Algunos jars, como por ejemplo servlet.jar, son aportados por el contenedor de aplicaciones, por lo que tendremos que definirlas marcándolas como provided. Esto le indica a Maven que estas librerías pueden ser utilizadas en tiempo de compilación, pero no serán agregadas al empaquetado final. Para declarar una dependencia de esta forma incorporaremos en nuestro pom.xml un código como este:

 

<dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
</dependency>
  • Librerías ya consideradas en las dependencias transitivas

Muchas de las dependencias dependen a su vez de otras librerías, por lo que Maven añadirá de forma transitiva nuevas librerías a nuestro proyecto. En estos casos no necesitamos definir estas nuevas dependencias transitivas en nuestro pom.xml. Como ejemplo, supongamos que un proyecto depende de estas librerías:

  • xalan-2-6.0.jar
  • xercesImpl
  • xml-apis

Al buscar la librería xalan-2-6.0.jar, observamos que la librería depende a su vez de las otras dos librerías. Por ello, para este ejemplo bastaría con declarar la dependencia a xalan-2-6.0.jar en nuestro pom.xml. Dependencias transitivas.

  • Conflictos entre librerías

En ocasiones nos podemos encontrar con que dos librerías tengan en sus dependencias a una misma tercera librería, pero de distinta versión. Ello haría que maven incluyese en nuestro Classpath 2 versiones de una misma librería, originando conflictos por duplicado de clases. En ese caso debe declararse en la dependencia de una de ellas una directiva para excluir la descarga de la librería conflictiva.

Por ejemplo, en un proyecto deseamos utilizar la librería siguiente:

 

<artifactId>jaxws-api</artifactId>
<groupId>javax.xml.ws</groupId>
<version>2.1-1</version>

Y tenemos dependencia a com.sun.xml.ws / jaxws-rt, esta librería se trae como dependencia transitiva otra versión de la librería mencionada anteriormente. Declaramos una exclusión para evitarlo:

 

<dependency>
  <groupId>com.sun.xml.ws</groupId>
  <artifactId>jaxws-rt</artifactId>
  <version>2.1.4</version>
  <exclusions>
    <exclusion>
      <artifactId>jaxws-api</artifactId>
      <groupId>javax.xml.ws</groupId>
    </exclusion>
  </exclusions>
</dependency>
  • Tenemos una librería que se encuentra en los repositorios, pero no hemos podido averiguar el número de versión.

En esta situación, si la librería no parece problemática, podríamos optar por elegir la última versión de la dependencia. En el caso de que consideremos que la versión que utiliza el proyecto puede originar problemas, lo mejor es añadir esta librería manualmente a nuestro repositorio (como si fuese una librería específica del proyecto), según se explica en el próximo punto.

  • La librería es específica del proyecto.

En estos casos tendremos que subir la dependencia a nuestro repositorio de librerías y, para evitar conflictos posteriores, se recomienda que sea subida con el mismo groupId de nuestro proyecto. Supongamos que migrando un proyecto nos topamos con librería example1.jar que no se encuentra en ningún repositorio público. En este caso la subimos a nuestro repositorio por ejemplo con la siguiente definición:

 

<dependency>
    <groupId>com.viavansi.examples</groupId>
    <artifactId>example1</artifactId>
    <version>0.0.1</version>
</dependency>

En la siguiente figura vemos cómo desplegamos una librería a Artifactory:

Artifactory

Comprobación de dependencias

Una vez que hayamos finalizado este proceso de definición de todas las dependencias en nuestro pom.xml, ejecutaremos en consola mvn dependency:tree (situados en el directorio donde se encuentre el fichero pom.xml del proyecto) para comprobar que las dependencias que se han incorporado al proyecto son las correctas. Este paso de verificación es muy importante en las migraciones de este tipo de proyectos, ya que es muy posible que alguna dependencia transitiva genere problemas. Como ejemplo, podemos observar la siguiente salida en la que comprobamos que no hay dependencias transitivas que entren en conflicto con alguna de nuestras librerías:

 

[INFO] [dependency:tree]
[INFO] com.viavansi.examples.examples1:war:0.0.1
[INFO] +- junit:junit:jar:3.8.1:test
[INFO] +- javax.servlet.jsp:jsp-api:jar:2.1:provided
[INFO] +- javax.servlet:servlet-api:jar:2.4:provided (scope not updated to compile)
[INFO] +- javax.activation:activation:jar:1.1:compile
[INFO] +- avalon-framework:avalon-framework-impl:jar:4.1.5:compile
[INFO] +- axis:axis-ant:jar:1.2.1:compile
[INFO] +- axis:axis:jar:1.2.1:compile
[INFO] +- javax.mail:mail:jar:1.3.1:compile
[INFO] +- commons-beanutils:commons-beanutils:jar:1.6:compile
[INFO] +- commons-codec:commons-codec:jar:1.3:compile
[INFO] +- commons-collections:commons-collections:jar:3.2:compile
[INFO] +- commons-dbcp:commons-dbcp:jar:1.2.2:compile
[INFO] +- commons-digester:commons-digester:jar:1.6:compile
[INFO] |  - xml-apis:xml-apis:jar:1.0.b2:compile
[INFO] +- commons-discovery:commons-discovery:jar:0.2:compile
[INFO] +- commons-httpclient:commons-httpclient:jar:3.1:compile
[INFO] +- commons-io:commons-io:jar:1.3.1:compile
[INFO] +- commons-lang:commons-lang:jar:2.2:compile
[INFO] +- commons-logging:commons-logging:jar:1.1:compile
[INFO] |  +- logkit:logkit:jar:1.0.1:compile
[INFO] |  - avalon-framework:avalon-framework:jar:4.1.3:compile
[INFO] +- commons-pool:commons-pool:jar:1.3:compile
[INFO] +- displaytag:displaytag:jar:1.1:compile
[INFO] +- displaytag:displaytag-export-poi:jar:1.1:compile
[INFO] +- dom4j:dom4j:jar:1.6.1:compile
[INFO] +- org.extremecomponents:extremecomponents:jar:1.0.1:compile
[INFO] +- com.lowagie:itext:jar:1.4.8:compile
[INFO] +- oro:oro:jar:2.0.7:compile
[INFO] +- javax.xml:jaxrpc-api:jar:1.1:compile
[INFO] +- javax.servlet:jstl:jar:1.1.2:compile
[INFO] +- log4j:log4j:jar:1.2.8:compile
[INFO] +- com.oracle:ojdbc14:jar:9.0.2.0.0:compile
[INFO] +- poi:poi:jar:2.5.1-final-20040804:compile
[INFO] +- javax.xml.soap:saaj-api:jar:1.2:compile
[INFO] +- org.springframework:spring:jar:2.0.5:compile
[INFO] +- taglibs:standard:jar:1.1.2:compile
[INFO] +- struts:struts:jar:1.2.9:compile
[INFO] |  +- commons-fileupload:commons-fileupload:jar:1.0:compile
[INFO] |  +- commons-validator:commons-validator:jar:1.1.4:compile
[INFO] |  - antlr:antlr:jar:2.7.2:compile
[INFO] +- wsdl4j:wsdl4j:jar:1.5.1:compile
[INFO] +- wss4j:wss4j:jar:1.5.0:compile
[INFO] +- xalan:xalan:jar:2.6.0:compile
[INFO] +- org.apache.xmlgraphics:xmlgraphics-commons:jar:1.2:compile
[INFO] +- xml-security:xmlsec:jar:1.2.1:compile
[INFO] +- org.directwebremoting:dwr:jar:2.0.3:compile
[INFO] +- com.viavansi:plantilla-client:jar:0.0.13:compile
[INFO] +- net.sourceforge.barbecue:barbecue:jar:1.5-beta1:compile

En caso de detectar librerías duplicadas a causa de las dependencias transitivas, deberemos proceder a definir las exclusiones necesarias en las dependencias afectadas, según se indicó anteriormente.

Redmine y Subversion (https): no actualiza las revisiones

Posteado por Manuel Navarro Almuedo en September 22nd, 2009

Hola a todos,

supongo que todos conoceis la fabulosa funcionalidad que tiene Redmine respecto al acceso a un sistema de control de versiones de código fuente, como el caso que nos ocupa: Subversion. Permite consultar las diferentes revisiones, mostrando los distintos comentarios de cada revisión, permite acceder a la estructura en árbol de directorios, etc…

En la situación que os quiero comentar teníamos una instalación de Redmine totalmente funcional, con la conexión a Subversion funcionando a las mil maravillas, vía https. Sin embargo de un día para otro dentro de la pestaña “Repositorio” del proyecto dejamos de ver la estructura de carpetas y ya no se actualizan la información acerca de las sucesivas revisiones que se iban generando en el repositorio, pero sí se veían otras revisiones anteriores.

El primer paso fue mirar en el log de Redmine y se comprobó que se estaba generando un error bastante “inquietante”:

Error parsing svn output: #<REXML::ParseException: No close tag for /lists/list>
…./jruby/lib/ruby/1.8/rexml/parsers/treeparser.rb:27:in `parse’
…./jruby/lib/ruby/1.8/rexml/document.rb:204:in `build’

………………….
………………….

Tras esto, nos conectamos con un cliente subversion y comprobamos que el repositorio estaba totalmente operativo y las revisiones se estaban generando correctamente (incluso con los respectivos comentarios): ¿qué podrá estar pasando? (si quereis ir directo a la solución sólo id al final del post xD)

Como suele ser muy habitual investigamos acerca de la conexión https que se establece con Subversión, ya que en este caso Subversion se sirve vía https. Pues bien, nos dimos cuenta que el certificado del servidor (el que sirve Subversion)  estaba caducado: ¡¡ Eureka !!

Para solucionar esto simplemente actualizamos el certificado del servidor Subversion y volvimos a intentar de nuevo consultar la pestaña Repositorio de Redmine, pero no… sigue el mismo problema, no se actualizan las revisiones.

Tras poner nuestras neuronas a trabajar nos dimos cuenta que no habíamos introducido el nuevo certificado como “confiable” para Redmine… muy bien, pero ¿cómo hacer esto? Para explicar esto voy a comentar cómo Redmine se conecta con Subversion y obtiene toda esa metainformación que muestra (esto lo podemos ver en el código fuente de Subversion.rb correspondiente al adaptador de Subversion que tiene Redmine):

Redmine no hace más que ejecutar la orden “svn –xml list <url_repositorio>” y esto devuelve (o debería) un XML con toda la información, algo tan simple como eso ¿cómo puede dar problemas?

Nos disponemos a ejecutar esa misma orden de forma manual desde la máquina donde tenemos instalado Subversion y ¡sorpresa!: no nos retorna un XML correcto. A continuación, ejecutamos un simple comando de listado “svn list <url_repositorio>” y obtenemos un mensaje de aviso, preguntándonos si deseamos rechazar / aceptar de forma permanente o temporal el certificado de https://servidorSubversion . Por supuesto lo aceptamos de forma permanente (pulsando ‘P’)  y a continuación nos pidela contraseña del usuario logado en el sistema y usuario/contraseña de acceso a Subversion.

A partir de ahí todo debería funcionar, pero no: hay un pequeño detalle más. Debemos repetir el paso anterior ejecutando el comando “svn”con el mismo usuario que hace correr el servicio de Redmine. Parece que el cliente svn tiene un almacen de certificados “confiables” distinto para cada usuario que lo ejecuta.

Una vez hecho todo esto nuestro Redmine se conecta perfectamente con Subversion y obtiene toda la información actualizada.

SOLUCION.- A continuación os resumo la solución con un par de pasos que se deben seguir, espero que os resulten útiles.

  1. Ejecutar por consola el comando “svn list <url_subversion>” en la máquina en que corre Redmine y con el mismo usuario que hace correr el servicio.
  2. En el diálogo que se muestra: aceptar el certificado como confiable de manera permanente (’P') e introducir los uusuarios/contraseñas que se solicitan.
  3. Ejecutar por consola el comando “svn –xml list <url_subversion>” y comprobar ahora que sí nos devuelve un XML con todos los metadatos.
  4. Comprobar que Redmine nos muestra ahora los datos correctos.

Versión de Redmine utilizada: Redmine 0.8.0.stable

Personalmente creo que este tipo de “errores” se deberían reportar de forma automática por parte de Redmine (mostrando una información más intuitiva) y es de esperar que en futuras versiones tomen en cuenta todos las numerosas incidencias que podeis ver en el bugtracker del proyecto Redmine respecto a este error.

Un Saludo a todos.
Namaste y buena suerte.

Tips: Mantis en Eclipse (plugin Mylyn-Mantis)

Posteado por Félix García Borrego en June 1st, 2009

Introducción

Mylyn-Mantis es un plugin que permite la integración en Eclipse con repositorios Mantis, de forma que se puede interactuar con un bugtracker Mantis directamente desde nuestro IDE, permitiendo:

  • Crear incidencias
  • Asignar incidencias
  • Añadir comentarios
  • Cambiar de estado incidencias
  • Resolver incidencias
  • Asociar un conjunto de código a una incidencia, a la hora de realizar un commit de código.

Utilización

Una vez que disponemos de un repositorio Mantis, podremos recuperar el conjunto de tareas de un proyecto, planificarlas, asignarlas, resolverlas, incorporar comentarios, asociar código fuente a las mismas…

Conexión a un repositorio

Gracias a Mylyn disponemos de dos nuevas vistas de interés:

  • Task Repositories: en esta vista configuramos los distintos repositorios (por ejemplo, un servidor de Mantis) que almacenan proyectos/tareas.
  • Task List: almacena tareas categorizadas de distintas formas (por ejemplo por proyectos).

Y como hemos dicho, gracias al plugin Mylyn-Mantis podemos utilizar Mantis dentro de Mylyn. Estas 2 vistas aparecen en la perspectiva “Planning”, perspectiva recomendada para trabajar con tareas.

En primer lugar deberíamos configurar el repositorio Mantis. Para ello necesitamos la vista Task Repositories. Esta vista podemos añadirla, si no la tenemos en nuestra perspectiva, a través de Window->Show View->Other, y desplegando la opción Mylyn. Dentro de esta vista, al hacer clic en el botón derecho del ratón nos aparece, entre otras, la opción de crear un nuevo repositorio de tareas (Add Task Repository):

Configuración de repositorio Mylyn

Al escoger esta opción se nos solicita qué tipo de servidor queremos configurar. En el caso de Mantis escogeremos dicha opción:

Añadir repositorio de tareas Maven con Mylyr

Y se nos piden datos sobre el servidor de Mantis:

Añadir repositorio de tareas Maven con Mylyr

Deberemos conocer la URL del servicio SOAP de Mantis (Mantis-Connect), así como nuestro usuario y contraseña. En esta ventana podemos realizar una validación de la configuración, para comprobar que sea correcta antes de grabar. Una vez hayamos hecho esta comprobación, ya disponemos de una conexión a Mantis.

Consulta a un repositorio y descarga de incidencias

Para ello creamos una query al repositorio Mantis. Dentro de la vista Task List, botón derecho del ratón, escogemos la opción “New Query”:

Añadir repositorio de tareas Maven con Mylyn

Escogeremos nuestro repositorio de tareas (Mantis) y continuamos. Le damos un título al filtro (normalmente el del proyecto con el que estemos trabajando) y en el desplegable escogemos el proyecto y un filtro. Estos filtros son queries que deben haber sido definidas previamente; es necesario disponer de al menos un filtro definido en Mantis, ya sea público o privado (un usuario, tras hacer una búsqueda avanzada, puede guardar ese filtro y recuperarlo en este momento; por ejemplo, puede crearse un filtro para recuperar sólo las incidencias no resueltas asignadas a él).

Añadir repositorio de tareas Maven con Mylyn

Al grabar se nos crea esta consulta, y el plugin comienza un proceso de sincronización con el servidor Mantis para descargarse todas las incidencias que cumplan el filtro:

listado de tareas

En cualquier momento podemos crear una nueva tarea dentro de Eclipse. Para ello, simplemente tenemos que invocar a New Task dentro de la vista Task List

Nueva tarea

Para crearla, nos pedirá primero en qué repositorio de tareas queremos grabarla (Mantis), luego pide el proyecto y se pulsa en Finish. Se llega a una ficha de la tarea donde podremos grabar sus datos. Esta ficha es idéntica a la que se observa al acceder al detalle de una tarea, donde podemos realizar prácticamente cualquier actividad sobre ella: cambiar su estado, asignación, dar comentarios, etc.

Editar tarea

Trabajando con tareas

Como se ha comentado, se recomienda el uso de la perspectiva Planning En la ficha de una tarea (parte superior derecha) disponemos de una opción para Activar / Desactivar una tarea:

Tareas en mylyn

Al activar una tarea, le indicamos a Eclipse que todo el código que incluyamos hasta el momento se corresponde con esa tarea, asociando el mismo al contexto de dicha tarea.

Si volvemos a nuestra perspectiva de trabajo (por ejemplo Java o Java EE, vista Project Explorer) es posible que dejemos de ver nuestros ficheros, advirtiendo un mensaje “Empty task context, unfocus or Alt + click”. Esto es debido a que Eclipse incorpora la posibilidad de filtrar nuestros proyectos/ficheros viendo sólo aquellos que están incluidos (relacionados) en el contexto de la tarea seleccionada:

Filtro

Simplemente pulsando sobre el icono de filtrado volveremos a ver todos los proyectos y ficheros, sin filtrar por el contexto de una tarea.

Esto por ejemplo provoca que se genere un comentario específico a la hora de hacer commit del conjunto de ficheros asociados al contexto de una tarea, muy útil para conocer qué tarea se ha resuelto con el conjunto de código entregado:

commit con mylyn

Planificando tareas

Eclipse nos permitirá planificar nuestras tareas asignadas en fechas, y nos avisará en pantalla en el momento en el que se supone que deberíamos desarrollar una tarea concreta.

Planificando tareas

Humor: Chuck Norris en el mundo Java

Posteado por Félix García Borrego en May 10th, 2009

Chuk norris domina Java

* Sólo Chuck Norris puede hacer una clase  abstracta y final.
* Chuck Norris serializa los objetos directamente en cráneos humanos.
* Chuck Norris no despliega aplicaciones web, él las mete a patadas en el servidor.
* Chuck Norris siempre utiliza sus propios patrones de diseño, y su favorito es la Patada Voladora Chuck.
* Chuck Norris puede usar para matarte cualquier clase de java.util .* , incluido el javadocs.
* Chuck Norris puede golpear tan fuerte que tu aplicación web se convierta en una aplicación swing, y es muy probable que sea una aplicación swing con una gran cantidad de iconos de cráneos humanos.
* Chuck Norris demostró el significado de Float.POSITIVE_INFINITY contando hasta él, dos veces.
* La sincronización no protege frente a Chuck Norris, si quiere el objeto, él lo toma.
* Chuck Norris no usa javac, él edita directamente los .class con un editor binario.
* El código Java de Chuck Norris nunca necesita ser optimizado. Su código es tan rápido que rompió la velocidad de la luz durante una prueba en los laboratorios de Sun matando a 37 personas.
* Cuando alguien intenta utilizar un método deprecated hecho por Chuck Norris , el método no avisa de que está deprecado. Automáticamente te pega una patada voladora en la cara en tiempo de compilación.
* El paquete java.lang originalmente contenía una ChuckNorris clase, pero fue quitado del paquete durante la revisión ya que Bill Joy recibió una patada voladora en la cara.
* Chuck Norris no tiene un error en su código, EVER!
* Chuck Norris no escribe código. Él mira a la pantalla de un ordenador hasta que obtiene el PROGRAMA que quiere.
* El código funciona más rápido cuando Chuck Norris lo mira.
* Chuck Norris modifica binarios .class ignorando el verificador de bycodes de Java.
* Chuck Norris no captura excepciones porque nadie tiene el coraje para lanzar ninguna.
* Chuck Norris puede hacer un casting a cualquier objeto sólo mirándolo.
* Si usted recibe un ChuckNorrisException lo más probable es que muera.
* Chuck Norris es el único que puede utilizar GOTO y const en Java.
* Chuck Norris puede compilar el código Java en . NET Framework, evidentemente, sólo mirándolo.
* Chuck no necesita capturar ninguna excepción de Java porque cuando va a lanzar la excepción Java tiene miedo de su patada voladora.
* Los niveles de visibilida Java son public,default, protected, private y protected By Chuck Norris “, no intente tener acceso a un campo con este último modificador!
* Chuck Norris come crudos JavaBeans y da patadas voladoras a JavaServer Faces!
* Chuck Norris puede dividir por 0!
* Recolector de basura sólo se ejecuta sobre el código de Chuck Norris para recoger los cadáveres.
* Cada línea de código de Chuck Norris se ejecuta en tiempo real. Incluso en una aplicación multi-thread.
* Cuando una CRU carga un .class de Chuck Norris duplica la velocidad.
* Chuck Norris puede ejecutar instrucciones de 64 bits en una CPU de 32 bits.
* Chuck Norris implementa “Indestructible”. Todas las demás criaturas implementan “Killable”.
* Chuck Norris sólo programa aplicaciones web en Java para conseguir ganar el .WAR!
* Chuck Norris dio una vez una patada voladora a una clase Java. El resultado es conocido como las inner class.
* Chuck Norris puede hacer herencia múltiple en Java.
* JVM no arroja excepciones a Chuck Norris, ya no. Que matara a 753 ingenieros de Sun fue suficiente.
* Chuck Norris no necesita unidad de pruebas, porque su código siempre funciona. Siempre.
* Chuck Norris extiende a Dios. ( Y Dios que era final no pudo hacer nada para evitarlo)
* Chuck Norris tiene tanta memoria de trabajo y es tan poderoso que puede ejecutar todas las aplicaciones Java en el mundo y obtener el 2% de uso de los recursos.
* Chuck Norris ya usaba en su código genéricos en la versión 1.3.
* Una clase Chuck Norris no puede ser decompilada… no se moleste en intentarlo.

Traducción libre de: http://www.ovisual.com/4/

Java / JEE : Firma digital y autenticación con Viafirma (I)

Posteado por Félix García Borrego en April 29th, 2009

Este artículo pretende ser una guía rápida qué explique cómo realizar una operación de autenticación con Viafirma, de cara a obtener los datos del certificado digital del usuario.

Aunque Viafirma ofrece todos sus servicios mediante métodos estándar (Servicios Web y OpenID), también disponemos  de un cliente para Java que permite de una forma muy sencilla integrar aplicaciones desarrolladas en esta tecnología con los servicios que ofrece Viafirma.

En este artículo mostraremos cómo añadir las dependencias necesarias a un proyecto web Java para hacer uso de los diferentes servicios de firma digital (XAdES, Facturae, PDF sign, etc… ), autenticación (FNMT, Camerfirma, Firma profesional, Ancert, ACA, Izempe,  DNIe, etc…),  custodia (integridad, etc…)  y verificación (CRLS, OCSP, etc…).

El procedimiento sería el siguiente:

1.- Añadir las dependencias

Viafirma está preparado para trabajar con Maven; en este tipo de proyectos sólo será necesario añadir la dependencia a viafirma-client de la siguiente manera:


<!--  Dependencias para el cliente viafirma con soporte de OpenID -->
<dependency>
 <groupId>org.viafirma</groupId>
 <artifactId>viafirma-client</artifactId>
 <version>[2.2.3,2.3.0)</version>
</dependency>

Y poner el repositorio de librerías de VIAVANSI para poder recuperar esta librería:

http://repositorio.viavansi.com/repo

Si el proyecto no está basado en Maven, necesitaremos añadir manualmente los .jar que se incluyen en el directorio dependency dentro del distribuible de viafirma-client.

2.- Crear la página de acceso a la autenticación

A modo de ejemplo básico vamos a crear una jsp que inicialice el cliente de Viafirma y permite al usuario iniciar el proceso de autenticación pulsando en un enlace. Este cliente utilizará el servidor público de pruebas de Viafirma desplegado en las instalaciones de Viavansi.

<%@page import=“org.viafirma.cliente.ViafirmaClientFactory”%>
<%@page import=“org.viafirma.cliente.ViafirmaClient”%>
<body>
<%
if (!ViafirmaClientFactory.isInit()) {
 // Configuración básica del cliente.
 ViafirmaClientFactory.init(“http://viafirma.viavansi.com/viafirma”,“http://viafirma.viavansi.com/viafirma”);
}

if(request.getParameter(“autenticar”)!= null){ ViafirmaClient viafirmaClient = ViafirmaClientFactory.getInstance(); // Iniciamos la autenticación indicando la uri de retorno. viafirmaClient.solicitarAutenticacion(request, response,“/viafirmaClientResponseServlet”); } %> <p><a href=“?autenticar=true”>Solicitar autenticación</a></p> </body>

Cuando el usuario pulse sobre el enlace “Solicitar autenticación” el usuario será redirigido a Viafirma, donde se le solicitará su certificado digital. Viafirma validará y tratará el certificado del cliente y retornará el resultado de la autenticación a la aplicación cliente que estamos desarrollando. En la jsp de ejemplo le indicamos a Viafirma que la url de retorno (donde Viafirma debe mandarnos el resultado de la autenticación) es /viafirmaClientResponseServlet .

3.- Procesar la respuesta

Una vez que Viafirma obtenga la información contenida en el certificado digital, retornará los datos a la url que la aplicación cliente le indicó, por lo que el siguiente paso será definir un servlet (cuya URL en este ejemplo es /viafirmaClientResponseServlet) que se encargue de gestionar la respuesta. Para ello crearemos un servlet que extiende de org.viafirma.client.ViafirmaClientServlet, e implementaremos los métodos error(…), cancel(…) y authenticateOK(…):


public class ViafirmaClientResponseServlet extends ViafirmaClientServlet{

@Override public void authenticateOK(UsuarioGenericoViafirma usuario,HttpServletRequest request, HttpServletResponse response) { // Lógica específica de la aplicación para gestionar el resultado de la autenticación request.setAttribute(“usuarioAutenticado”, usuario); request.getRequestDispatcher(“/resultadoAutenticacion.jsp”).forward(request, response); }

@Override public void cancel(HttpServletRequest request, HttpServletResponse response) { // Gestiónn de cancelaciónn del usuario al autenticar o firmar request.setAttribute(“error”, “El usuario ha cancelado la autenticación”); request.getRequestDispatcher(“/resultadoAutenticacion.jsp”).forward(request, response); }

@Override public void error(CodigoError codError, HttpServletRequest request, HttpServletResponse response) { // Gestión de error al autenticar o firmar request.setAttribute(“codError”, codError); request.getRequestDispatcher(“/resultadoAutenticacion.jsp”).forward(request, response); }

}

En este ejemplo vemos un ejemplo de implementación, en el que la aplicación simplemente guarda en request el objeto UsuarioGenericoViafirma que contiene todos los datos que aparecen en el certificado digital. Obviamente cada aplicación, en función de su lógica de negocio, deberá realizar la implementación específica que requiera.

4.- Adaptar la plataforma al skin de la aplicación cliente.

La página donde se solicita el certificado digital reside en Viafirma. Sin embargo, a través de CSS podremos conseguir que el usuario no aprecie un cambio de interfaz, de forma que el salto de la aplicación a Viafirma parezca transparente a nivel estético.

Para hacer que Viafirma se adapte fácilmente al estilo visual de nuestra aplicación cliente, sólo tendremos que colocar el fichero viafirmaStyle.css en el raíz de nuestra aplicación y redefinir el aspecto visual de la interfaz  de Viafirma.

5.- Descarga el cliente y pruébalo tu mismo

Descargar ejemplo de autenticación utilizando el cliente Java para obtener los datos del certificado digital.

Próximamente: Java / JEE : Firma digital XADES y facturae con Viafirma (II)

Hudson. Parte 2. Crea tus propios plugins

Posteado por Eduardo Fernández León en April 28th, 2009

Una de las ventajas de Hudson es la posibilidad que nos ofrece de ampliar su funcionalidad mediante su sistema de plugins. De esta forma, podemos realizar acciones sobre los builds, tales como generación de informes, controladores, acciones sobre el código fuente, etc. En Internet podemos encontrar algunas páginas de donde descargar plugins para Hudson, como por ejemplo http://hudson.gotdns.com/wiki/display/HUDSON/Plugins.

 

Aún disponiendo de una cantidad considerable de plugins en la red que cubran la mayoría de las necesidades, nos podemos encontrar en la situación de tener que crear uno propio. Si te has planteado desarrollar uno te habrás dado cuenta de que la documentación en Internet sobre cómo crear un plugin es muy escasa, estando el grueso de la información en sólo dos páginas: http://hudson.gotdns.com/wiki/display/HUDSON/Plugin+tutorial y http://javaadventure.blogspot.com/2008/01/writing-hudson-plug-in-part-1.html.

 

Pues bien, en Viavansi surgió la necesidad de crear un plugin para Hudson que subiera el código fuente de un proyecto a un repositorio de Subversion, de forma que se automatizara esta tarea cada vez que Hudson hiciera un build.

 

El plugin constará de una parte de configuración en donde el usuario encargado de cada proyecto introducirá la url del repositorio de Subversion donde desea subir el código, los parámetros relativos al repositorio (nombre de usuario y password) así como dos entradas para ficheros que no se quieren subir o ficheros que no se quieren sobreescribir en el destino. Además dispondrá de una opción para habilitar o deshabilitar el plugin en todos los proyectos de forma global.

 

 

Creación de la estructura del plugin

 

Lo primero que vamos a tener que usar para crear el plugin es la herramienta Maven, la cual nos creará la estructura de paquetes, además de algunos ficheros de ejemplos. Esto era de esperar, pues Hudson internamente usa Maven para la creación de los builds de los proyectos.

 

Empezamos situandonos en una consola sobre nuestro directorio de proyectos y tecleamos:

 

mvn hpi:create

 

Después de pedirnos algunos datos como el identificador del grupo y el nombre del plugin, Maven nos creará la siguiente estructura:

 

+ src

+ main

+ java

+ groupid.name

+- PluginNamePublisher.java

+- PluginImpl.java

+ resources

+ groupid.name.project

+- config.jelly

+- global.jelly

+- index.jelly

+ webapp

+- help-globalConfig.html

+- help-projectConfig.html

 

Por defecto, se crean unos ficheros de ejemplo (el archiconocido HelloWorld) que podemos usar para ver cómo está implementado un plugin sencillo.

 

Si queremos tener soporte en nuestro IDE para la implementación del plugin deberemos, en el caso de Eclipse, tendremos que introducir lo siguiente:

 

mvn -DdownloadSources=true eclipse:eclipse

 

Si tenemos otros entornos como Netbeans o IntelliJ, existen plugins específicos de Maven para estos IDEs.

 

Bien, acabamos de crear la estructura de nuestro primer plugin, nos falta decirle a Hudson qué tipo de plugin hemos desarrollado, agregar la lógica y crear los campos visuales para que se añadan a las páginas de configuración de los proyectos.

 

 

Creando el plugin

 

Uno de los aspectos más importantes en la creación de un plugin para Hudson consiste en determinar el tipo de proyecto para el que va a ser utilizado. En nuestro caso utilizamos proyectos de tipo Maven, por lo cual necesitamos publicar el plugin en Hudson a través de un Publisher; en caso contrario (para proyectos de tipo Ant, por ejemplo) necesitaríamos publicarlo usando un Builder. Otra de las características de usar un Publisher frente a un Builder consiste en que la acción se ejecuta cuando un proyecto ha terminado de hacer el build.

 

Para publicar el plugin mediante un Publisher basta con tocar la clase PlugImp quedando de la siguiente forma:

 

public class PluginImpl extends Plugin {

public void start() throws Exception {

//Publicamos el descriptor del plugin

Publisher.PUBLISHERS.add(SVNCopyPublisher.DESCRIPTOR);

}

}

 

En este código podemos ver como se ha modificado la clase inicialmente creada por Maven, añadiendo la línea donde le decimos a Hudson que registre nuestro plugin. Debido a que la funcionalidad que queremos desarrollar consiste en copiar el código fuente de un proyecto a un repositorio de Subversion, el nombre de nuestro plugin será SVNCopyPlugin (original, verdad?), creando así la clase SVNCopyPublisher que será la que contenga el descriptor y la lógica de negocio.

 

Los elementos más importantes de la clase SVNCopyPublisher son:

 

  • Atributos necesarios para la ejecución de la lógica del plugin. En nuestro caso, url del repositorio, nombre de usuario, contraseña y listas con los ficheros que no queremos subir o que no queremos sobreescribir. Cada atributo deberá tener un método público get y set.
  • Un constructor público en el que pasaremos los valores de los campos rellenados por el usuario en la configuración del plugin e inicializaremos los atributos definidos en el punto anterior. Además, este constructor deberá estar anotado con @DataBoundConstructor.

  • Un método perform en donde se colocará la lógica de nuestro plugin.

  • Un atributo estático y final llamado DESCRIPTOR que será instancia de una clase interna de nombre DescriptorImpl.

A su vez, la clase DescriptorImpl contendrá la configuración global del plugin. En el caso de SVNCopyPlugin hemos usado en la configuración local un checkbox con el cual activaremos el uso del plugin y sólo realizará la copia cuando esté activado.

 

Además esta clase debe contener:

 

  • Los atributos propios de la configuración global del plugin, con sus gets y sets.

  • Un constructor protected sin parámetros.

  • El método configure que hará persistente la configuración global.

  • Un método newInstance que devolverá una instancia de nuestra clase Publisher con los valores introducidos por el usuario.

Hasta aquí ha sido todo teoría, veamos ahora como queda la clase SVNCopyPublisher y su clase interna DescriptorImpl:

 

public class SVNCopyPublisher extends Publisher {

private Boolean enable;

private final String urlTarget, nameTarget, passTarget, filesTargetException, filesSourceException;

@DataBoundConstructor

public SVNCopyPublisher(String urlTarget, String nameTarget, String passTarget, String filesSourceException, String filesTargetException) {

this.urlTarget = urlTarget;

this.nameTarget = nameTarget;

this.passTarget = passTarget;

this.filesTargetException = filesTargetException;

this.filesSourceException = filesSourceException;

}

//GETTERS y SETTERS de los atributos

public boolean needsToRunAfterFinalized() {

return true;

}

/*

* Contiene la lógica del plugin, que se ejecutará cuando termine el build de Hudson

*/

@Override

public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {

if(enable && build.getResult().equals(Result.SUCCESS)){

//Aquí debe ir la lógica de negocio del plugin

}

return true;

}

public Descriptor<Publisher> getDescriptor() {

return DESCRIPTOR;

}

public Action getProjectAction(AbstractProject< ?, ?> project) {

return null;

}

/**

* Descriptor should be singleton.

*/

public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

/**

* Descriptor for {@link SVNCopyPublisher}. Used as a singleton.

* The class is marked as public so that it can be accessed from views.

*/

public static final class DescriptorImpl extends Descriptor<Publisher> {

 

/**

* If you don’t want fields to be persisted, use transient

*/

private boolean enable;

protected DescriptorImpl() {

super(SVNCopyPublisher.class);

load();

}

/**

* This human readable name is used in the configuration screen.

*/

public String getDisplayName() {

return “SVNCopy Plugin Project Configuration”;

}

/**

* Get the fields from the global configuration form and persist them.

*/

public boolean configure(StaplerRequest req, JSONObject o ) throws FormException {

enable = o.getBoolean(“enable”);

save();

return super.configure(req,o);

}

 

/**

* Creates a new instance of {@link SVNCopyPublisher} from a submitted form.

*/

@Override

public SVNCopyPublisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {

SVNCopyPublisher pub = req.bindJSON(SVNCopyPublisher.class, formData);

pub.setEnable(enable);

return pub;

}

public boolean isEnable(){

return enable;

}

}

}

 

 

Del código de arriba podemos destacar el método perform, que es el encargado de llamar a la lógica del plugin, recibiendo como parámetros una instancia de AbstractBuild (usado para lanzar el plugin si el build ha terminado satisfactoriamente), una de Launcher que contiene datos de la máquina que realiza el build y otra de Listener, que entre otras cosas contiene el logger con el cual podemos imprimir en la consola web de Hudson nuestra salida del plugin. Otros métodos importantes son configure y newInstance que reciben como parámetros la petición del formulario de configuración y los datos introducidos por el usuario en forma de JSON. El parámetro StaplerRequest forma parte del framework Stapler utilizado por Hudson para la aplicación web y es útil si queremos obtener datos de la petición usando getParameter.

 

Generando los formularios y uniéndolos al código del plugin

 

Para terminar con el desarrollo de nuestro plugin nos falta crear los formularios de entrada en la parte de configuración global de Hudson, así como en la configuración particular de cada proyecto. Maven nos crea por defecto los ficheros global.jelly y config.jelly, además de unos htmls de ayuda. Estos ficheros siguen un esquema XML basandose en un framework ligero llamado Jelly http://commons.apache.org/jelly/.

 

Para comenzar, tenemos que incluir en la configuración global de Hudson el checkbox que habilitará de forma general el uso del plugin para todos los builds que se hagan (además se dispone de otro checkbox que habilita el plugin para cada proyecto). Dentro de la carpeta resources se encontrarán como mínimo tres ficheros .jelly: index, global y config. El primer fichero contiene una descripción del plugin y en los otros dos se deberá colocar los elementos htmls para la configuración global y específica del mismo.

 

En el fichero global.jelly queremos colocar el checkbox que hará que se active el plugin para todos los proyectos. Para ellos basta con colocar las siguientes lineas:

 

<j:jelly xmlns:j=”jelly:core” xmlns:st=”jelly:stapler” xmlns:d=”jelly:define” xmlns:l=”/lib/layout” xmlns:t=”/lib/hudson” xmlns:f=”/lib/form”>

 

<f:section title=”SVNCopyPlugin Global Configuration”>

 

<f:entry title=”Enable”

description=”Check if you want to perform a copy of your project from your repository to another one”>

 

<f:checkbox name=”enable” checked=”${descriptor.isEnable()}” />

 

</f:entry>

</f:section>

 

</j:jelly>

 

Como se puede apreciar el código es bastante autoexplicativo. Se usan unos tags propios de Jelly para añadir el checkbox a la configuración de Hudson (Manage Hudson / Configure System). El valor del checkbox estará bindeado al atributo enable ya visto en el descriptor.

 

 Configuración global del plugin

 

Por otra parte el fichero config.jelly contendrá la configuración específica del plugin para cada proyecto. Su contenido será el siguiente:

 

<j:jelly xmlns:j=”jelly:core” xmlns:st=”jelly:stapler” xmlns:d=”jelly:define” xmlns:l=”/lib/layout” xmlns:t=”/lib/hudson” xmlns:f=”/lib/form”>

<!–

Cajas de texto para introducir las url de los dos repositorios, los nombres y los passwords

–>

<f:entry title=”URL del repositorio destino”>

<f:textbox field=”urlTarget” value=”${descriptor.urlTarget}”/>

</f:entry>

<f:entry title=”Nombre de usuario del repositorio destino”>

<f:textbox field=”nameTarget” value=”${descriptor.nameTarget}”/>

</f:entry>

<f:entry title=”Password del repositorio destino”>

<f:password field=”passTarget” value=”${descriptor.passTarget}”/>

</f:entry>

<f:entry title=”Ficheros y carpetas que no se desean sobreescribir en el destino”>

<f:textbox field=”filesTargetException” value=”${descriptor.filesTargetException}”/>

</f:entry>

<f:entry title=”Ficheros y carpetas que no se desean subir al destino”>

<f:textbox field=”filesSourceException” value=”${descriptor.filesSourceException}”/>

</f:entry>

</j:jelly>

 

Gracias a este fichero se incluirá en la configuración del proyecto las cajas de texto para que cada usuario introduzca sus datos sobre el repositorio. Cada elemento f:textbox leerá/escribirá su valor en un atributo del descriptor.

 

 Configuración del plugin para cada proyecto

 

 

Ejecución y debug

 

Con todo esto ya hemos terminado de programar el plugin, ahora sólo queda ejecutarlo y ver que todo va como deseamos. Para ello usaremos el comando mvn hpi:run desde la carpeta del plugin, el cual nos montará un Hudson en nuestra máquina y con el que podremos trastear antes de instalarlo en el Hudson de la empresa.

 

También es interesante la opción de debuguear para seguir la traza y ver qué valores van tomando cada uno de los elementos que intervienen en la ejecución del plugin. Para tener la posibilidad de debuguear antes de llamar a mvn hpi:run tendremos que setear las variables de entorno con los comandos:

 

export MAVEN_OPTS=”-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n”

 

si usamos Unix o

 

set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n

 

si usamos Windows.

 

 

Empaquetando y exportando el plugin

 

Para finalizar sólo queda crear el fichero .hpi para importarlo desde el Hudson instalado en uno de los servidores de la empresa, algo tan sencillo como ejecutar mvn hpi:package y coger el fichero con esta extensión creado en la carpeta target.

 

Con esto ya tienes para empezar a customizar tu Hudson añadiendole la funcionalidad que necesitas y no te la dan otros plugins. Ya se sabe, si no está: hazlo tu mismo.

 

 

Próximamente…

 

En breve pondremos una entrada explicando cómo sacarle más partido a los plugin de Hudson, añadiendo interacción con el usuario, generación de informes del plugin por cada build, etc.

Implementando una caché sencilla de objetos con Ehcache

Posteado por Javier Echeverría Usua en March 14th, 2009

Reciéntemente he estado revisando la arquitectura técnica de un proyecto Java EE que estaba teniendo serios problemas de escalabilidad. En Desarrollo todo iba como un tiro, pero con un número importante pero no escandaloso de registros (apenas 100.000) nos encontrábamos con tiempos de respuesta de algunas de las operaciones online que se podían ir a los 5 minutos. Obviamente esos tiempos no son admisibles para una operación web, por lo que urgía detectar fuentes de problemas y optimizar los procesos.

Realmente no había ninguna parte del código culpable al 100%, sino que la funcionalidad implementada, excesivamente flexible para el usuario,  le permitía a éste realizar búsquedas y operaciones que finalmente implicaban una sucesión de consultas, filtrados, operaciones de unión/intersección de listas enormes, persistencia…

Para muchas de las listas que se manejaban, se podía garantizar su invariabilidad en un tiempo elevado. Datos, por ejemplo, relativos a provincias. Por ello daba la sensación de que implementar una caché de estas listas, y sobre todo de los resultados finales después de las mencionadas operaciones de unión/intersección, etc., podría ser oportuno. Es cierto que Hibernate permite caché de resultados (y de hecho utiliza Ehcache), pero en este caso hablamos de resultados post-procesados, por lo que no sería suficiente.

Cachear objetos en una aplicación Java EE es algo básicamente trivial: siempre disponemos de la opción de ubicar el objeto en contexto de aplicación (ServletContext) y recuperarlo cuando queramos. Sin embargo esta aproximación no deja de ser algo “de juguete”. Decidimos utilizar Ehcache en primer lugar porque sabíamos que Hibernate sacaba un buen provecho de él, pero sobre todo porque, con un desarrollo extremadamente sencillo, obtenemos importantes ventajas funcionales como:

  • Gestión de caducidad del contenido. Pasado un tiempo determinado, la caché elimina el objeto cacheado y comienza a devolver NULL a las peticiones posteriores. Nos elimina la responsabilidad de persistir el tiempo de vida de un objeto cacheado.
  • Configuración del máximo número de objetos a cachear en memoria. Podemos dimensionar la cantidad de objetos que deseamos cachear en memoria. Ehcache se encarga de serializar a disco todo lo que le sobra, y recuperarlo cuando sea necesario. Ni que decir tiene que todos los objetos a cachear deberán ser serializables.
  • Posibilidad de despliegue en cluster.
  • Eliminación de la caché durante el shutdown del servidor de aplicaciones.
  • ¡No reinventar la rueda!

Al final desarrollamos una sencilla clase tipo Manager, implementando el patrón Singleton, que interactúa con Ehcache. Y en determinadas zonas de la aplicación, invocar a la cache para recuperar los objetos que anteriormente se generaban online.

Paso a poner el código, que al final sin chicha esto se queda en nada :-) Eso sí, me gustaría recalcar que el proceso de 5 minutos al final, con un poco de cariño, ha pasado a responder en menos de 10 segundos. Una ganancia de un 3000%…

/*
* File: CacheUtil.java
*
* Created on march 2009
*
*
* Copyright 2006-2029 Javier Echeverría Usúa (javieu at gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.viavansi.framework.core.cache;


import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


import com.viavansi.framework.core.persistencia.servicios.excepciones.ExcepcionServicio;


/**
* @author Javier Echeverria Usua (javieu at gmail.com)
* @author Felix Garcia Borrego (borrego at gmail.com)
* @author Alexis Castilla Armero (pencerval at gmail.com)
*/
public class CacheUtil {


public Object getCachedObject(String key) throws Exception{


if(minutosVidaDefault == 0)
minutosVidaDefault = MINUTOS_DEFAULT;

return getCachedObject(key, minutosVidaDefault);
}

public void putCachedObject(String key, Object info) throws Exception{

if(minutosVidaDefault == 0)
minutosVidaDefault = MINUTOS_DEFAULT;

putCachedObject(key, info, minutosVidaDefault);
}

public Object getCachedObject(String key, int minutosCache) throws Exception{

Cache cacheCustodia = getInstance(minutosCache);
Element element = cacheCustodia.get(key);
if(element != null){
return cacheCustodia.get(key).getValue();
}
else{
return null;
}
}

public void putCachedObject(String key, Object info, int minutosCache) throws Exception{

Cache cacheCustodia = getInstance(minutosCache);
cacheCustodia.put(new Element(key,info));
}

private Cache getInstance(int minutosCache) {
Cache cache = null;
//Todavía no existe
int timeTolife = minutosCache * 60;

CacheManager manager = CacheManager.getInstance();

String nombreCache = "viavansiCachedInfo" + minutosCache;

if (manager.cacheExists(nombreCache)) {
cache = manager.getCache(nombreCache);
} else {
// Configuramos el apagado en caso de Error de la JVM o parada inesperada.
System.setProperty("net.sf.ehcache.enableShutdownHook", "true");
manager.addCache(new Cache(nombreCache, 100, true, false, timeTolife, timeTolife));
cache = manager.getCache(nombreCache);
}

return cache;
}

public static void shutDown() {
CacheManager.getInstance().shutdown();
}

private int MINUTOS_DEFAULT = 60;

private static int minutosVidaDefault = 0;

protected CacheUtil(int minutosVida) {
minutosVidaDefault = minutosVida;
}

public static void init(int minutosVida) {
minutosVidaDefault = minutosVida;
}

/*
* Constructor y accesores
*/

// Patrón Singleton
private static CacheUtil singleton;

//Instancia de Commons Logging
private Log log = LogFactory.getFactory().getInstance(this.getClass().getName());

/**
* private constructor (solo una instancia).
* @return AmbitoBO
*/
protected CacheUtil() throws ExcepcionServicio {
super();
log.debug("Creando nueva instancia de CacheUtil");
}

/**
* Devuelve instancia activa de AmbitoBO.
* Típico método de implementación de patrón singleton
* @return AmbitoBO
* @throws ExcepcionServicio
*/
public static CacheUtil getCurrentInstance() throws ExcepcionServicio {
if (singleton == null) {
try {
singleton = new CacheUtil();
} catch (ExcepcionServicio e) {
throw e;
}
}
return singleton;
}

}

Un saludo a todos, y poned una caché en vuestros corazones :-P

Leyendo un feed OpenSearch con código Java

Posteado por Javier Echeverría Usua en November 25th, 2008

De forma muy resumida, OpenSearch es una colección de formatos abiertos y estándares desarrollados por A9 (Amazon), que persiguen resolver 2 escenarios principalmente:

  • Caso 1: Permitir a una aplicación publicar de una forma estándar resultados de búsqueda; estos resultados pueden ser consumidos posteriormente por una aplicación cliente. Se basa en un metamodelo estandarizado publicado bajo una fuente de sindicación estándar como RSS o Atom. Podemos ver un ejemplo de resultado de búsqueda en formato OpenSearch en esta URL de indeed.com.
  • Caso 2: Describir (autodescribir) servicios de búsqueda. Es lo que utiliza por ejemplo Firefox 3 para ofrecernos búsquedas inteligentes.

En este post vamos a describir cómo leer resultados de búsqueda en formato OpenSearch (caso 1). Para el caso 2, recomendamos la lectura de un post de 11870.com que describe a la perfección cómo describir nuestro buscador.

Si nos fijamos en el código fuente de respuesta de la URL comentada de indeed, veremos que en este caso se está devolviendo sobre RSS una información general sobre la búsqueda más un conjunto de resultados de búsqueda (items), los cuales pueden ser incluso geolocalizados.

En Java, este RSS (o Atom) podría ser leído con cualquier intérprete de XML, o mejor con alguna librería específica de este tipo de feeds, como Rome o Apache Abdera, o mejor aún, con librerías específicas para OpenSearch. En nuestro caso, debíamos leer la respuesta del API de 11870.com, dentro de un proyecto en el cual deseábamos proponer establecimientos propuestos por los usuarios que estén cercanos a nuestra localización.

A este respecto, cuando he hecho la búsqueda de librerías Java, personal he tenido una sensación agridulce. Si bien los desarrolladores Java solemos ser unos verdaderos privilegiados en cuanto a lo que se refiere a disposición de API’s, en ciertos casos relacionados con web semántica o API’s de web 2.0 tengo la sensación de que otras arquitecturas de desarrollo como PHP, Python o RoR a veces tienen cierta ventaja. En el caso de Java todo son versiones de incubadora, 0.X, etc.

En el caso de librerías Java, una rápida búsqueda nos llevó a decidirnos entre Rome (Sun) y Apache Abdera, escogiendo este último por disponer de mejores ejemplos de los que partir. Además dentro de los committers de Abdera está el supercrack David Calavera, desarrollador de la omnipresente 11870.com.

A continuación incluiremos unos pequeños recortes de código que muestran como leer la respuesta OpenSearch mediante Abdera. En primer lugar, necesitaremos disponer de las librerías. Para ello (en el caso de Maven) incluiremos estas dependencias en nuestro pom.xml:

<dependency>
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-client</artifactId>
<version>0.4.0-incubating</version>
</dependency>

<dependency>
<groupId>org.apache.abdera</groupId>
<artifactId>abdera-extensions-opensearch</artifactId>
<version>0.4.0-incubating</version>
</dependency>

Para poder resolver estas dependencias incluiremos los repositorio Incubating y Snapshot de Apache dentro de <repositories>:

<repository>
<id>apache-incubating</id>
<name>Apache Incubating Repository</name>
<url>http://people.apache.org/repo/m2-incubating-repository/</url>
</repository>
<repository>
<id>apache-snapshots</id>
<name>Apache Snapshot Repository</name>
<url>http://people.apache.org/repo/m2-snapshot-repository/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>

Dado una dirección URL (String urlQuery) donde esté publicada una consulta que responde OpenSearch, podremos invocarla con el siguente código:

Abdera abdera = new Abdera();
Parser parser = abdera.getParser();
URL url = new URL(urlQuery);
Document<Feed> doc = parser.parse(url.openStream(), urlQuery);
Feed feed = doc.getRoot();

Podemos recuperar ciertas propiedades de la respuesta de OpenSearch:

IntegerElement totalResults = feed.getExtension(OpenSearchConstants.TOTAL_RESULTS);
int resultados = totalResults.getValue();

Del mismo modo se pueden recuperar datos como el número de items por página (OpenSearchConstants.ITEMS_PER_PAGE) o el índice inicial de la respuesta, útil cuando hay paginación (OpenSearchConstants.START_INDEX. Es decir, podemos obtener datos OpenSearch del feed mediante feed.getExtension().

A continuación mostramos cómo iterar los items de respuesta del feed:

for (Entry entry : feed.getEntries()) {

    System.out.println(”Title: “+entry.getTitle());
    System.out.println(”Summary: “+entry.getSummary());
    System.out.println(”Id: “+entry.getId().toString());
    System.out.println(”Id: “+entry.getId().toString());

}

También podemos evaluar para cada item (resultado de búsqueda) el valor de extensiones a OpenSearch. Por ejemplo, podemos pretender recuperar los datos de geolocalización en formato GeoRss. Para ello utilizaríamos un código como el que sigue:

QName qnameWhere = new QName(”http://www.georss.org/georss/10″,”where”, “georss”);
Element where = entry.getExtension(qnameWhere);
try {
String geoPos = where.getFirstChild().getFirstChild()
.getText();
String[] posicion = geoPos.split(” “);
if (posicion.length == 2) {

System.out.println(”latitud: “+posicion[0]);
System.out.println(”longitud: “+posicion[1]);
}
} catch (Exception e) {
e.printStackTrace();
}

Es decir, podemos recuperar el valor de un parámetro de una extensión invocando a entry.getExtension, pasando como parámetro un objeto de tipo QName, instanciado como indicamos en el código anterior.

En definitiva, hemos hecho una breve introducción a OpenSearch y mostrado código de ejemplo para interactuar con una respuesta OpenSearch mediante Apache Abdera.

Conclusiones finales. Eligiendo nuestro entorno de Integración Continua (V).

Posteado por Félix García Borrego en November 17th, 2008

Después de una serie de artículos que se iniciaron con Eligiendo nuestro entorno de Integración Continua (I) , tenemos las conclusiones finales de nuestra comparativa.

  • Tabla resumen

A modo de resumen hemos agrupado en una sola tabla todos las parámetros evaluados en la comparativa, para permitirnos una comparación rápida entre las diferentes herramientas.

 

Apache
Continuum

LuntBuild

Hudson

Instalación
Documentación
de instalación

Suficiente

Buena

Buena.
Con ayuda contextual

Dificultad
de la instalación

Fácil.
Requiere la configuración de recursos JNDI ( SMTp, base de datos,
etc.)

Fácil.
puede requerir configuración de ficheros externos

Muy
Fácil. Es posible su ejecución directamente o sobre cualquier
servidor de aplicaciones ligero, sin requerir edición de ficheros
de configuración

Configuraciones
avanzadas

Compleja

Fácil

Fácil

Administración
¿permite la
configuración completa desde la interfaz Web?

Sí.
A excepción de algunos parámetros como el SMTp, conexión base
de datos, parámetros JNDI, …

Completa

Completa

Documentación
oficial de Administrador

Suficiente

Suficiente

Buena.
Con ayuda contextual

¿permite
copias de Seguridad desde la herramienta?

No

No

Mantiene
un histórico de compilaciones (build)

Gestión de
tareas programadas

Rendimiento

Bueno

Muy
bueno, permite gestión de hilos de ejecución y tareas
distribuidas

Muy
bueno, permite gestión de hilos de ejecución y tareas
distribuidas

Seguridad
Gestión de
permisos basados en perfiles

Sí,
pero sólo con un conjunto de perfiles predefinido.

Gestión de
permisos específicos por proyecto

Facilidad de
configuración de la seguridad

Fácil

Fácil

Muy
Fácil

Integración
con sistemas externos
Integración
con Sistemas de control de Versiones

Suficiente

Muy
Buena

Muy
Buena

Integración
con plataformas de gestión de incidencias (bugtrackers)

Mala

Mala

Regular

Integración
con herramientas de generación de informes (reporting)

Buena,
basada en Maven

Buena

Muy
Buena. plugins Hudson integrados

Desarrollo
de nuevos plugins o mecanismos de extensión

Complejo
(Malo)

Suficiente

Muy
Bueno

Integración
con herramientas de testeo

Buena

Buena

Muy
buena, con agregación de los resultados en la propia interfaz
web.

Tipos de
proyectos Soportados
proyectos
Java

Muy
Buena

Muy
Buena

Excelente
(permite un gran número de posibilidades)

proyectos No
Java

Mala.
Solo basados en Shell scripts

Mala.
Solo basados en Shell scripts

Buena,
mediante plugins

Facilidad
de uso

Documentación
de usuario

Buena

Buena

Muy
Buena, con ayuda contextual

Interfaz

Fácil

Complejidad
media

Muy
fácil

Estabilidad
Estabilidad
general de la herramienta

Buena

Buena

Buena

¿permite
compilación distribuida?

No

¿permite la
gestión de hilos de ejecución?

No

  • Conclusiones finales.

Sin duda nuestra propuesta se inclina por Hudson. Brevemente vamos a enumerar y resumir (de entre los detalles de esta comparativa) ciertos aspectos que hacen inclinar la balanza hacia este software:

  • Extensibilidad. La posibilidad de desarrollar plugins que permitan adaptar e inyectar funcionalidad de forma personalizada es una característica casi imprescindible.  Apache Continuum no dispone de esta posibilidad de integrar plugins (deben ser plugins Maven, utilizados específicamente en cada uno de los proyectos Java), lo cual prácticamente hace descartar esta plataforma. LuntBuild dispone de ciertas opciones de inyección de código, si bien a este respecto las mejores características las tiene Hudson.
  • Calidad de la documentación. En esta materia Hudson también dispone de la mejor documentación, por delante de LuntBuild y Continuum.
  • Integración de resultados de plugins (centralizados o Maven) en pantalla. Un aspecto de interés es que el resultado de los diversos plugins pueda verse en la interfaz web de la aplicación. Tan sólo Hudson proporciona este nivel de integración por defecto.
  • Sistema de seguridad y permisos. En esta funcionalidad tan importante lideran la comparativa Hudson y Continuum. Ambas disponen de la posibilidad de gestión de permisos basados en perfiles, y posibilidad de personalizar los permisos por proyectos. Continuum puede requerir modificaciones en ficheros de configuración y reinicio del sistema, mientras que Hudson permite configuración integral en pantalla.
  • Estabilidad. Las tres plataformas disponen de buenas características de estabilidad, si bien destacan Hudson y LuntBuild, que permiten la gestión multihilo y compilación distribuida.
  • Soporte de la comunidad. A este respecto lideran la comparativa tanto Hudson como Continuum, desarrollos de Sun y Apache respectivamente.

Como se puede observar, Hudson auna las principales características que nosotros esperabamos de un sistema de Integración Continua, y por ello es nuestra propuesta y elección para servir de plataforma base en la infraestructura de desarrollo.