domingo, 4 de noviembre de 2012

Repositorios en Integración Contínua

Uno de los apartados a destacar en la integración continúa es el uso del repositorio. Es decir, el buen uso.

Cualquier herramienta se puede utilizar para el bien o para el mal, y ciertamente un repositorio mal utilizado nos puede llevar al fracaso por una de las vías más rápidas.

Son frecuentes los casos de versiones incorrectas, clases que no compilan, casos de prueba que dejan de funcionar, y errores q el lunes estaban solucionados y sospechosamente vuelven a escena el viernes.

Una de las principales normas parece ser: subir al repositorio código compilable, probado y compatible con el resto de módulos.

Otros casos curiosos son los de aquellos desarrolladores que parecen inmersos en su propio mundo, con su copia local de código a la que deberían llamar "Mi Tesoro", y que sólo liberan cuando hay ciertas alineaciones planetarias. Tan perjudicial como el que sube código corrupto.


Es por tanto importante establecer las normas del juego antes de iniciar el trabajo con un repositorio.

Otro de los aspectos importantes a determinar en la elección del repositorio es si el repositorio estará alojado en la propia compañía, o si será un servicio proporcionado por un tercero. 


  • En el primer caso habrá que considerar los costes del servicio así como el mantenimiento (instalación, backups, etc ). 
  • En el segundo caso habrá que estudiar las características de los distintos proveedores de dicho servicio.

 También es importante establecer cual va a ser el software de gestión del repositorio, subversion (svn) o git son los más comunes hoy en día. Tendremos que charlar más ampliamente sobre estos en otro momento.




A la hora de elegir el repositorio es bueno haber probado alguno, yo por esto me decanto por un repositorio gratis, con lo que puedo probar a un cierto proveedor y posteriormente puedo contratar el plan adecuado para mis necesidades. 

Una comparativa sobre repositorios svn lo podéis encontrar en svn repository hosting.

 

lunes, 21 de mayo de 2012

Utilizando el log en Gradle

Hemos visto que los ficheros de gestión de proyecto de gradle no son más que código, con lo que podemos sacar notificaciones por consola utilizando la sentencia println:

println sourceSets.main.java.srcDirs

Como ya hemos visto en un post sobre java, utilizar la sentencia println para mostrar mensajes de estado por consola (o fichero) no nos proporciona mucha flexibilidad, por tanto una buena política es la de usar un framework que nos permita elegir la granularidad de los mensajes de log.

Con gradle podemos tener distintos niveles de log, utiliza SLF4J, y así podemos diferenciar los mensajes que queremos ver cuando se realiza la construcción de nuestro proyecto de aquellos que se mostrarán cuando estramos preparando el build.gradle y algo no funciona exactamente como deseamos.

Los niveles de log con que contamos son:

  • ERROR Muestra los mensajes de error.
  • QUIET  Nivel específico de Gradle, no muestra mensajes. Si ejecutamos gradle en este nivel (opción –q al ejecutar), sólo se mostraran mensajes configurados para este nivel. Es el nivel más “silencioso”
  • WARNING Muestra los mensajes de advertencia
  • LIFECYCLE Nivel específico de Gradle, habitualmente son los mensajes de gradle respecto al proceso de construcción. Si ejecutamos gradle es el nivel por defecto.
  • INFO Muestra los mensajes de información
  • DEBUG Muestra los mensajes de depuración

Cambio de nivel de log en la línea de comandos

Cuando ejecutamos gradle podemos elegir en qué nivel de log se va a realizar la ejecución.

Las opciones a nivel de línea de comandos son:

  • <defecto> Si no configuramos ninguna opción se mostrarán los niveles LIFECYCLE y superiores, esto es ERROR, QUIET, WARNING y LIFECYCLE.
  • -q Se mostrarán los niveles QUIET y superior.
  • -i Se mostrarán los niveles INFO y superior.
  • -d Se mostrarán los niveles DEBUG y superior, con lo que cualquier mensaje de log saldrá por consola.

Escribiendo mensajes de log

Como vimos podemos escribir en el fichero build.gradle:

println ‘Texto de log con println’

Usando la propiedad logger de cualquier script de construcción de proyectos (build.gradle) podemos codificar en qué nivel queremos que salgan los mensajes:


logger.quiet("Mensaje que siempre se muestra")
logger.error("Mensaje de nivel ERROR")
logger.warn("Mensaje de nivel WARNING")
logger.lifecycle("Mensaje de nivel LIFECYCLE")
logger.info("Mensaje de nivel INFO")
logger.debug("Mensaje de nivel DEBUG")

Si tecleamos gradle en la línea de comandos podemos ver:

>gradle
Mensaje que siempre se muestra
Mensaje de nivel ERROR
Mensaje de nivel WARNING
Mensaje de nivel LIFECYCLE
:help

Welcome to Gradle 1.0-rc-3.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

BUILD SUCCESSFUL
Total time: 5.266 secs


Si queremos ver todos los mensajes de log tecleamos gradle –d
                  
El resultado es un tanto extenso, por eso no lo muestro.


Creando nuestro propio logger

Hemos visto que podemos utilizar la propiedad logger para mostrar nuestros mensajes de notificación, pero como nuestros ficheros de configuración de proyecto son código, podemos crear nuestro propio logger. De esta forma creamos un fichero build.gradle de la siguiente forma:


import org.slf4j.Logger
import org.slf4j.LoggerFactory

Logger slf4jLogger = LoggerFactory.getLogger("MiLogger")
slf4jLogger.warn("Mi propio Logger ha sido creado")



El resultado obtenido es:

>gradle
Mi propio Logger ha sido creado
:help

Welcome to Gradle 1.0-rc-3.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

BUILD SUCCESSFUL

Total time: 3.719 secs


martes, 15 de mayo de 2012

Integrando gradle y eclipse


En el post anterior estuvimos viendo esta fantástica herramienta para construcción de proyectos de software (Java) que es Gradle. Teníamos desarrollado un pequeño proyecto, y copiamos el código fuente de este proyecto a las carpetas oportunas donde gradle toma por defecto el código y el código de las pruebas unitarias.  Es decir, comenzamos la casa por el tejado.

Creando proyectos eclipse desde Gradle

Comencemos por el principio, en mi caso utilizo Eclipse como IDE, lo que voy a hacer es crear un proyecto que pueda importar fácilmente en Eclipse y después configurar Eclipse para poder correr Gradle desde el IDE.

Primero creamos un fichero build.gradle.

apply plugin: 'eclipse'
repositories {
    mavenCentral()
}


Este fichero de construcción nos va a ayudar a crear la estructura adecuada de ficheros y carpetas para poder importar en eclipse el proyecto.

Ejecutamos gradle eclipse y vemos en consola lo siguiente

E:\Gradle\eclipse>gradle eclipse
:eclipseProject
:eclipse

BUILD SUCCESSFUL

Total time: 4.359 secs


Como vimos también podemos incluir algunas dependencias:

apply plugin: 'java'
apply plugin: 'eclipse'
repositories {
    mavenCentral()
 
dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

Ejecutar gradle eclipse

E:\Gradle\eclipse>gradle eclipse
> Building > :eclipseClasspath > Resolving dependencies 'detachedConfiguration1'
:eclipseClasspath
Download http://repo1.maven.org/maven2/commons-collections/commons-collections/3.2/commons-collections-3.2-sources.jar
:eclipseJdt
:eclipseProject
:eclipse

BUILD SUCCESSFUL

Total time: 8.984 secs


Las diferencias en el primer proyecto, y el segundo son claras, ya que en el segundo proyecto puedes ver como se incluye el classpath a las librerías necesarias. Nos únicamente queda importarlo en Eclipse mediante File->Import->Existing proyects into workspace.

Ejecutando Gradle desde Eclipse

Se puede configurar Eclipse para ejecutar Gradle, los pasos que debemos seguir son los siguientes:

Run -> External Tools -> External Tools Configurations...
Seleccionar Program, en el menu de la izquierda, y crear una nueva configuración con el botón de arriba a la izquierda.
Procedemos a configurar cómo se ejecutará Gradle:
Name: entramos Gradle (por ejemplo) como nombre de la aplicación que lanzaremos.
Location: tenemos que seleccionar GRADE_HOME\bin\gradle.bat
Working Directory: introducimos ${project_loc} para que ejecute gradle en el proyecto en el que estemos trabajando.
Arguments: introducimos ${string_prompt} para que pregunte por un nombre de tarea para gradle cuando sea ejecutado (por ejemplo como vimos en el anterior post: build, o como hemos visto en este post eclipse).


martes, 8 de mayo de 2012

Descubriendo GRADLE



Gradle es una de estas herramientas que te da buen feeling desde que la descubres, te permite gestionar y construir proyectos de software (Java). Te ofrece la versatilidad de Ant, la rigidez de Maven, tiene muy buena documentación, y además los scripts de construcción de tu proyecto no son xml sino que son código.

Instalación

Instalar Gradle teniendo una jdk cuya versión  sea superior a 1.5 es tan sencillo como descomprimir el zip que puedes descargar de www.gradle.org, añadir GRADLE_HOME/bin a la variable de entorno PATH. Con esto sólo tienes que teclear gradle-v y comprobar que sale algo como:

------------------------------------------------------------
Gradle 1.0-rc-3
------------------------------------------------------------
Gradle build time: domingo 29 de abril de 2012 23H51' UTC
Groovy: 1.8.6
Ant: Apache Ant(TM) version 1.8.2 compiled on December 20 2010
Ivy: 2.2.0
JVM: 1.7.0_03 (Oracle Corporation 22.1-b02)
OS: Windows XP 5.1 x86


Java plugin

Una vez instalado nos centramos en crear nuestro primer proyecto, yo por mi parte he cogido un pequeño proyecto en el que demostraba como generar dos clases y ejecutar las pruebas unitarias de estas dos clases.

Como mencionaba gradle nos permite introducir código (groovy) en nuestros scripts de construcción de proyectos, podemos incluir sentencias como las siguientes para sacar por consola la configuración de gradle:

println "Code" //Código principal del proyecto
println sourceSets.main.java.srcDirs
println "Classes" //Clases generadas del proyecto principal
println sourceSets.main.output.classesDir

println "Tests" // Código de pruebas del proyecto
println sourceSets.test.output.classesDir


Usando la configuración por defecto gradle asume que la estructura del proyecto es la siguiente:

src/main/java  código fuente java
src/test/java  código fuente de las pruebas

Con lo que depositamos ahí nuestro código desarrollado anteriormente.

Gradle está construido basado en plugins, iremos viendo algunos de ellos, para este primer ejemplo veremos el plugin Java. Este plugin añade capacidades de compilación, pruebas y empaquetamiento a un proyecto.

Para declarar la utilización del plugin sólo tenemos que incluir la sentencia:

apply plugin: 'java'

Declaramos así mismo los repositorios del proyecto, en nuestro caso reutilizamos los repositorios de maven.

repositories {
    mavenCentral()
}

Añadimos las dependencias al proyecto. Como vemos hay dos grupos de dependencias, por un lado las de la tarea de compilación y por otro lado las de la tarea de pruebas unitarias.

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

Podemos declarar también la generación de un archivo jar, que por defecto se genera de la siguiente forma:

[baseName]-[appendix]-[version]-[classifier].[extension]

Siendo baseName, appendix, version, classifier y extension atributos, las tareas en gradle son objetos, de la clase Jar. Igual podemos construir el fichero manifest:

jar {
    baseName= 'junitBlog'
    version = '1.0'
    manifest {
        attributes 'Implementation-Title': 'Atherindarl project', 'Implementation-Version': version
    }
}

Creo un jar llamado junitBlog-1.0.jar.

Juntando los diversos elementos en un único fichero build.gradle

apply plugin: 'java'


println "Code"
println sourceSets.main.java.srcDirs
println "Classes"
println sourceSets.main.output.classesDir
println "Tests"
println sourceSets.test.output.classesDir

jar {
    baseName= 'junitBlog'
    version = '1.0'
    manifest {
        attributes 'Implementation-Title': 'Atherindarl project', 'Implementation-Version': version
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

test {
    systemProperties 'property': 'value'
}



Tecleando gradle build vemos como las distintas tareas de gradle se van ejecutando.

>gradle build

Code
[E:\Gradle\junit\src\main\java]
Classes
E:\Gradle\junit\build\classes\main
Tests
E:\Gradle\junit\build\classes\test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 9.484 secs


Gradle nos ofrece la estructura de tareas básicas que puede tener un proyecto: compilar, probar, generar jar. Podemos añadir código a nuestro build.gradle, y diversos plugins dependiendo de nuestras necesidades que nos permiten una gran flexibilidad.


lunes, 30 de abril de 2012

Ventajas y Desventajas de la integración contínua

Después de unas cuantas experiencias positivas podríamos hacer una larga lista de ventajas, y también después de tener alguna experiencia no tan positiva puedes darte cuenta de algunas piedras que puedes llevar en el zapato y que te hacen algo más complicado el camino.

Empezando por las desventajas:
  • Cultura de la organización, sin ánimo de extenderme mucho, construir el caso de uso de la integración contínua en una organización cuyos procedimientos no incluyen pruebas unitarias puede ser algo duro.
  • Tiempo de configuración inicial, en un entorno en el que se diseñan, desarrollan y documentan adecuadamente los casos de prueba, sólo nos queda invertir el tiempo oportuno en la configuración de las herramientas de integración continua. Si no disponemos de pruebas unitarias, es más conveniente empezar por ahi.
  • Buenas pruebas unitarias, cierto que el papel lo soporta todo, pero en ocasiones las pruebas unitarias también. Hacer revisión de código de las pruebas unitarias, o ideas simples como un desarrollador realiza el código y otro las pruebas de ese código te dará resultados sorprendentes. En cierta ocasión recuerdo que para irse pronto a casa los desarrolladores hacían que las pruebas siempre fueran bien (AssertTrue(true)).
  • Costes de hardware, es necesario un conjunto de máquinas que albergarán las diversas herramientas para la integración contínua: repositorio, automatización, entornos, …
  • Costes de personal de QA, además de las máquinas habrá personal que se encargue de la configuración, seguimiento, realización y generación de informes de las pruebas. La ventaja es que cualquiera puede ver el resultado de las pruebas con lo que los propios desarrolladores pueden corregir aquellas partes del código que no sean correctas, o los gestores de proyecto controlar la calidad del producto construido.
Leyendo la sección de desventajas podemos ver un apocalipsis de problemas y costes generados por una nueva metodología, o ver los diferentes escollos que vamos a encontrar en el camino y pensar con antelación la adecuada manera de enfocar la implantación.
Siendo positivos, el vaso normalmente siempre está lleno con diversos porcentajes de líquido y aire sobre el, la integración contínua tiene también unas ventajas que podemos enumerar de la siguiente manera:
  • En caso de que fallen las pruebas unitarias, el desarrollador puede volver a la versión del código sin error y detectar más fácilmente el error. Se evita el problema conocido como infierno de integración, en el que se juntan todos los módulos y se comienzan a depurar los errores. De hecho se van corrigiendo los problemas de integración más continuamente, de ahí el nombre de la metodología.
  • Se detecta pronto la existencia de código incompatible, ya que cada cierto tiempo se toma el código del repositorio y se vuelve a construir cada módulo y se van realizando las pruebas sobre él. Por la misma razón se encuentran cambios en el código que crean conflictos.
  • Continuamente se realizan las pruebas unitarias sobre todos los cambios.
  • Facilidad para realizar pruebas de regresión.
  • Disponibilidad constante de una versión comprobada de la rama de código en la que se está trabajando actualmente, de forma que se puede realizar una demostración, o tener una versión del producto que resuelve ciertos fallos del producto detectados.
  • Los desarrolladores tienen acceso a la información de las pruebas de forma que en caso de fallo pueden reaccionar más rápidamente que esperando a las pruebas de integración, cuando se acaba la fase de desarrollo, o pruebas de aceptación del usuario, a veces meses después del desarrollo.
  • Como gestor, o promotor del proyecto, una de las mejores ventajas es que se pueden generar KPIs del proyecto en desarrollo y se puede vigilar que no se superen ciertos márgenes y así controlar el desarrollo del proyecto en base a datos fiables.

En resumen más ventajas que inconvenientes, aunque cierto es que en una organización en la que la integración continua está consolidada y la cultura de la organización tiene embebida esta metodología. En caso contrario, la recomendación sería ir piano piano, y dejar que la organización se empape de las diversas mejores prácticas, y se engrasen los engranajes. Correr demasiado lleva al fracaso.

martes, 27 de marzo de 2012

Integración Continua

Hablar del concepto de integración continua sin mencionar en el primer parrafo a Martin Fowler es un poco presuntuoso como poco, Martin ha sido durante años el campeón, como el mismo dice, de la integración continua.

La integración continua consiste en la compilación de los módulos de un proyecto, y la ejecución de las pruebas de dichos módulos de una forma frecuente. 


Compilación


Realizar la compilación de los módulos de un proyecto es algo que podemos hacer manualmente o bien utilizando nuestro entorno de desarrollo favorito, o bien utilizando herramientas como Ant o Maven. 


Ejecución de pruebas


En los post anteriores hemos visto como realizar pruebas de código mediante el empleo de un framework bastante popular como es junit. Vímos también como utilizar Ant para la ejecución de estas pruebas.


Introducción


En proyectos pequeños sabemos realizar tanto la compilación, en los entornos de desarrollo podemos hacerlo automáticamente con cada cambio, como las pruebas.


En proyectos medianos, se complica la situación cuando hay varios módulos y varios desarrolladores trabajando en ellos, ya que la integración entre ellos se complica, y sobretodo los cambios en el código pueden hacer que todo el proyecto deje de cumplir adecuadamente los requisitos. 


Por esto, el planteamiento de la integración continua. Un entorno en el que mediante determinadas mejores prácticas, metodologías y herramientas se llega a solventar la problemática de la integración de los módulos de software, a veces conocida como el infierno de la integración.


Metodologías


No hay metodologías per se que se desarrollen para el concepto de integración continua, sin embargo si hay ciertas metodologías de desarrollo que cohabitan con el concepto de integración continua como la programación extrema, así como desarrollo ágil, y más recientemente scrum.


La metodología de gestión de proyectos waterfall no se adapta bien al concepto de integración continua, siendo la integración continua en el proceso de desarrollo.


Mejores Prácticas


  • Mantener un repositorio de código.
  • Automatizar la construcción.
  • Automatizar las pruebas.
  • Realizar commit al repositorio diariamente.
  • El proyecto se debe poder construir con cada commit, el desarrollador debe ejecutar pruebas en su entorno local antes de hacer el commit.
  • La construcción del proyecto debe ser rápida.
  • Realizar las pruebas en una copia del entorno de producción.
  • Debe ser sencillo obtener los últimos entregables.
  • Cualquiera puede ver los resultados de la última construcción.
  • Automatiza la instalación (deployment).

Herramientas


Existe una amplia variedad de herramientas que nos van a ayudar en la implementación de estas mejores prácticas.
  • Repositorios de código: cvs, svn y git.
  • Gestión de proyecto: maven2.
  • Automatización de la construcción: ant, maven2.
  • Automatización de las pruebas: junit, dbunit, xunit, testng.
  • Automatización de la integración continua: cruisecontrol, hudson.

martes, 20 de marzo de 2012

Pruebas unitarias con junit (II)

En el post anterior me quedé un poco corto, junit nos permite muchas más cosas. Expliqué que las pruebas unitarias deben ser independientes, ahora veremos como podemos hacer esto.

En el caso que nos ocupaba, cada prueba era autocontenida, los datos que se utilizaban formaban parte de la clase, más aún, del método de prueba. Vemos que los operadores de la operación de suma están contenidos en el código del método.

public void testAdd100 (){
int num1 = 3;
int num2 = 2;
int total = 5;
int sum = 0;

sum = Mathematica.add(num1, num2);
assertEquals(sum, total);
}

En otras ocasiones nos enfrentaremos con clases que tenemos que inicializar, bases de datos que deben estar en un determinado estado antes de realizar la prueba, y que deben estar en un determinado estado al terminar la prueba (o pruebas).

Junit nos provee con dos métodos que podemos sobreescribir

setup: se ejecuta al principio de la ejecución de la clase de pruebas, en el podemos inicializar clases, abrir conexiones, insertar datos, cargar ficheros, ...

teardown: se ejecuta después de ejecutar el último test de la clase de pruebas, en el podemos restaurar los valores según se hayan definido para el fin de la prueba.

Vamos a probar una clase que necesita configuración inicial:

public class Configuracion {

private int rowNumber;

public Configuracion (int valor){
this.rowNumber=valor;        
}

public int getRowNumber (){
return  this.rowNumber;
}

public void setRowNumber ( int valor ){
this.rowNumber=valor;
}
}


Definimos dos casos de prueba

En el primero crearemos un valor para el rowNumber, y validaremos que este valor está correctamente establecido.

En el segundo estableceremos un valor para el rowNumber, y validaremos que este valor está correctamente establecido.

Creamos la clase de prueba

public class TestConfiguracion extends TestCase{

protected Configuracion configuracion;

public void setUp (){
configuracion = new Configuracion (5);
}

public void testConfiguracion001 () {
int c= configuracion.getRowNumber();
int inicial = 5;

assertEquals(c,inicial);
}

public void testConfiguracion002 () {
int valor = 3;
configuracion.setRowNumber(valor);
int c= configuracion.getRowNumber();

assertEquals(c,valor);
}
}

Para crear la clase configuración hemos de asignar un valor, algo que hacemos en el método setUp(). Así para el primer test está establecido con el valor que fijamos en la llamada al constructor.

Algo que no mencioné, los métodos de prueba se ejecutan en orden alfabético.

En la segunda prueba, nos aseguramos que el método setRowNumber funciona adecuadamente, ya que en la anterior hemos validado que tanto el constructor como getRowNumber funcionan adecuadamente.

¿Cómo se entiende que en un blog en español haya escrito la clase que vamos a probar en ingles? Sólo para remarcar una idea, he partido de unos requerimientos y mi parte inglesa ha escrito la función que los cumple, y mi parte castellana la función que los valida. Algo bastante habitual en grandes proyectos.

Para ejecutar esta clase de test he creado un target para ant de la siguiente manera



                 








Teniendo el resultado

testConfig:

[junit] Running com.blog.TestConfiguracion
[junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0,016 sec

sábado, 17 de marzo de 2012

Vamos hacia delante

Habitualmente 3 años despues de una versión principal, 1 año despues de la siguiente versión principal, 6 meses despues de que la subsiguiente versión es tomada como JRE por defecto en java.com una versión mayor de Java llega al final de su vida.

Despues de empezar por el tejado, lleguemos a las paredes. Java 6 va a llegar al final de su vida el 6 de Noviembre de 2012. Lo que implica todo el esfuerzo de migración, si fuera necesaria, hacia versiones posteriores y por otro lado la mejora, aunque en ocasiones se ve más bien como "mejora", las nuevas características de la versión 7 y la versión 8.

Un interesante artículo (en ingles) sobre la llegada al final de la vida de una versión de Java para aquellos que no tienen muy claro lo que esto significa.

jueves, 15 de marzo de 2012

Hay vida después de la adquisición

Es cierto que un alto porcentaje de las adquisiciones, al final tienen la pinta de que se compra la compañía xyz porque “molesta” y se cierra y listos. Otras al final no llevan a donde se esperaba, y se acaba revendiendo lo que se compró por un precio muy inferior al inicial, y acaba todo de una forma un tanto agridulce.

Ultimamente he leido un par de noticias que son algo más alentadoras, ya que al principio quizá tenía más pinta de pez grande se come a pez pequeño, que de otra cosa.

Hablo de la última versión de Mysql Cluster 7.2 y Java Server Faces.

En el caso de Mysql, parecía que la gran compañía de bases de datos compraba a la compañía que más pegaba últimamente en el opensource, y se auguraba un futuro bastante negro. Pero ahí estamos con una última versión mejorada, y más rápida. Veremos como seguimos por aqui.

En el case de JavaServer Faces, el advenimiento de html5 y que adobe ha dejado de apostar, en cierta medida, por flash/flex/as (llamemoslo x) nos hace también ser positivos.

martes, 13 de marzo de 2012

Pruebas unitarias con junit

Si has oído hablar de pruebas de software, has oído hablar de junit, incluso de alguno de sus sucesores, xunit, etc.

Junit es un framework de pruebas, es decir toda la parafernalia que vas a necesitar poner en tu código java para poder hacer pruebas unitarias de software.

Una prueba unitaria es la prueba de un módulo de software, como puedes adivinar cuantos más módulos tenga una aplicación, más pruebas tendremos que realizar. Si las pruebas las podemos automatizar de alguna manera las podremos repetir tantas veces como sea necesario y dedicar nuestro tiempo a revisar los informes de pruebas y retocar la aplicación, más que a probar y probar, generar los informes, revisar los informes y retocar la aplicación.

Acabo de describir el proceso de integración continua, descargar del sistema de control de versiones el fuente de la aplicación, compilarlo, y realizar las pruebas unitarias y de integración de los módulos. Al ir probándose el código cada cierto tiempo, no nos encontraremos con las sorpresas de último momento.

Volviendo a las pruebas unitarias, ¿cuáles son las características de una prueba unitaria?

  • Independiente: una prueba no debe afectar a las otras.
  • Completa: deben cumplir la mayor parte, o la totalidad, del código.
  • Repetible: una prueba tiene que poderse ejecutar tantas veces sea necesario.
  • Automatizable: una prueba debe ejecutarse sin intervención manual.
  • Profesional: una prueba no deja de ser código, por lo que deben estar documentadas, y bien diseñadas.

Existen varios frameworks de pruebas, testng, junit, y otros, aunque ahora veremos como utilizar junit.

Primero tenemos que tener claro que porción de código queremos probar, en nuestro caso será esta nueva clase que estoy desarrollando:

public class Mathematica {

public static int add (int a, int b){
return a+b;
}

}

Los requerimientos, que tenemos que tener en cuenta tambien en la fase de pruebas unitarias, nos dicen que la funcion add aplicada sobre dos números enteros nos devolverá la suma aritmética de ambos.

Por ello nos disponemos a crear nuestros casos de prueba, vamos a crear 4 casos:

  1. 3+2 = 5
  2. 3+0 = 3
  3. 0+2 = 2
  4. 3+2 != 4

Creamos el típico caso de “happy path”, o que seguro sabemos que podemos cumplir. Muchos programadores nos quedamos aquí, pero tenemos que probar tambien ciertos límites como la suma con cero, y que el valor que obtenemos es diferente de cualquier otro.

Para estas pruebas básicas usamos dos funciones del framework junit:

  • assertEquals(x,y) que compara dos valores x e y (está implementado para casi todos los tipos de datos.
  • assertFalse(z) que verifique un valor booleano.

Tenemos que crear una clase que extienda la clase del framework TestCase:

public class TestMathematica extends TestCase

Y dentro aquellos métodos de prueba correspondientes a los casos de prueba, estos métodos de prueba comienzan con la palabra test.

Por ejemplo, para el primer caso de prueba:

public void testAdd100 (){
int num1 = 3;
int num2 = 2;
int total = 5;
int sum = 0;
sum = Mathematica.add(num1, num2);

assertEquals(sum, total);
}


Para realizar la ejecución de los casos de prueba hay varias formas, con eclipse, netbeans, maven, y ant, por poner algunos ejemplos. Por ejemplo veamos como hacerlo con ant



                 






Al ejecutar la clase TestMathematica resultará:

testMath:
[junit] Running com.blog.TestMathematica
[junit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 0 sec

El código completo de la clase de pruebas es:

public class TestMathematica extends TestCase {

public void testAdd100 (){
int num1 = 3;
int num2 = 2;
int total = 5;
int sum = 0;
sum = Mathematica.add(num1, num2);

assertEquals(sum, total);
}

public void testAdd200 (){
int num1 = 3;
int num2 = 0;
int total = 3;
int sum = 0;
sum = Mathematica.add(num1, num2);
assertEquals(sum, total);
}

public void testAdd300 (){
int num1 = 0;
int num2 = 2;
int total = 2;
int sum = 0;
sum = Mathematica.add(num1, num2);
assertEquals(sum, total);
}

public void testAdd400 (){
int num1 = 3;
int num2 = 2;
int total = 4;
int sum = 0;
sum = Mathematica.add(num1, num2);
boolean condition = sum==total;
assertFalse(condition);
}

}

jueves, 8 de marzo de 2012

Cifrado datos

Una de las cuestiones importantes hoy en día son los diferentes aspectos de la seguridad, cifrado de datos, firmas digitales, etc. En un sólo post me parece mucho recorrer todos los aspectos, por tanto, para ir abriendo boca empezaremos con un simple cifrador que podemos usar para proteger de miradas indiscretas aquella información de nuestra preferencia.

Primero tenemos que definir una clave que usaremos para cifrar los datos:

            byte[] iv = new byte[]{
                    (byte)0x8E, 0x12, 0x39, (byte)0x9C,
                    0x6F, 0x6F, 0x6F, 0x5A
            };

Después tendremos que crear dos objetos de cifrado, uno para cifrar y otro para descifrar:

            Cipher ecipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            Cipher dcipher = Cipher.getInstance("DES/CBC/PKCS5Padding");


Después de inicializarlos sólo tenemos que copiar un stream de entrada en otro de salida de la siguiente forma:

a) Para cifrar:

        out = new CipherOutputStream(out, cipher);
        copyStreams(in,out);

b) Para descifrar:

        in = new CipherInputStream(in, cipher);
        copyStreams(in,out);

Bueno, el código completo es el siguiente:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.spec.AlgorithmParameterSpec;
import java.util.logging.Logger;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class Encrypt {

    private final static Logger myLogger= Logger.getLogger( Encrypt.class.getName() );
    private static int tamBuffer=2048;

    protected static void copyStreams (InputStream in, OutputStream out){
        byte[] buffer = new byte[tamBuffer];
        
        try {
            // Leer del stream de entrada, y escribir en el de salida usando el buffer.
            int numLeidos = 0;
            while ((numLeidos = in.read(buffer)) >= 0) {
                out.write(buffer, 0, numLeidos);
            }
            out.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void encrypt(Cipher cipher, InputStream in, OutputStream out) {
        myLogger.info("Iniciando encriptado de datos...");
        out = new CipherOutputStream(out, cipher);
        copyStreams(in,out);
        myLogger.info("Finalizado encriptado de datos...");
    }

    public static void decrypt(Cipher cipher, InputStream in, OutputStream out) {
        myLogger.info("Iniciando desencriptado de datos...");
        in = new CipherInputStream(in, cipher);
        copyStreams(in,out);
        myLogger.info("Finalizado desencriptado de datos..."); 
    }
    

    public static void main(String[] args) {

        String ficheroACodificar="fichTxt.txt";
        String ficheroCodificado="fichCod.cod";
        String ficheroOriginal="fichOrig.txt";

        //Creamos las claves asimétricas
        try{

            SecretKey key = KeyGenerator.getInstance("DES").generateKey();

            //Creo una clave para la generación de los Cipher
            byte[] iv = new byte[]{
                    (byte)0x8E, 0x12, 0x39, (byte)0x9C,
                    0x6F, 0x6F, 0x6F, 0x5A
            };

            AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);

            Cipher ecipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            Cipher dcipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

            // Creamos los dos cifradores, para cifrar y para descifrar.
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

            encrypt(ecipher, 
                    new FileInputStream(new File(ficheroACodificar)), 
                    new FileOutputStream(new File( ficheroCodificado)));
            
            decrypt(dcipher,
                    new FileInputStream(new File(ficheroCodificado)),
                    new FileOutputStream(new File(ficheroOriginal)));
            
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

}

martes, 6 de marzo de 2012

Tecnología de log de java

Explicaré como usar el API de logging que viene por incluido en el entorno de desarrollo de Java.

Como indicaba el entorno de desarrollo, JDK, contiene el API de logging de java. A través del logger se puede enviar el texto que queremos anotar o bien a un determinado fichero, a la consola, etc.. El API de logging permite configurar la manera en que estos mensajes son anotados, se pueden distinguir distintas categorías o prioridades (severe, warning, info, config, fine, finer, finest), se pueden formatear de distintas formas, sobre que clase(s) queremos anotar información, etc...

Los pasos a seguir son

a) Crear un Logger.
b) Seleccionar el nivel de anotación.
c) Seleccionar el Manejador.
d) Formato de los mensajes.

a) Crear un logger

Crear un logger es sencillo, ya que solamente tenemos que utilizar la clase Logger del paquete java.util.logging de la siguiente manera:

import java.util.logging.Logger;

private final static Logger MyLogger= Logger.getLogger( MyClass.class.getName() );

b) Nivel de anotación

Tenemos los siguientes niveles de anotación, de menor a mayor importancia:

FINEST
FINER
FINE
CONFIG
INFO
WARNING
SEVERE

Con esto podemos decidir con qué nivel de importancia damos a los mensajes que queremos
anotar. El funcionamiento de las anotaciones es entonces el siguiente:

i) En tiempo de desarrollo determinamos la importancia de las anotaciones que queremos sacar, o bien por consola o bien por fichero.
ii) Durante la ejecución se puede gestionar el nivel mínimo que vamos a querer anotar. A partir de el nivel seleccionado todos los mensajes serán anotados.

Además de los niveles mencionados se puede establecer el nivel ALL, que anota todo, y OFF, que no anotará nada.

c) Selección del Manejador

Una vez creado el logger, podemos asignar tantos manejadores (handlers) como necesitemos para nuestros propósitos.

Así pues, cada manejador (handler) recibe los mensajes que debe anotar y los muestra en el destino que hayamos elegido.

Por defecto existen dos manejadores:

i) Consola: escribe el mensaje en la consola. ConsoleHandler.
ii) Fichero: escribe el mensaje en un fichero. FileHandler.


d) Formato de los mensajes

El formato de los mensajes a anotar, se aplica mediante el uso de un formateador a la salida de cada handler.

Hay dos clases que nos ayudan a dar formato a los mensajes:

i) SimpleFormatter: nos ayuda a generar mensajes de log como texto.
ii) XMLFormatter: nos ayuda a generar mensajes de log en formato XML.

Estas dos clases se pueden extender y generar nuestros propios formateadores.

Yendo directamente a ver un ejemplo de código:

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class TestingLogger {

private final static Logger myLogger= Logger.getLogger( TestingLogger.class.getName() );

public static void main(String[] args) throws SecurityException, IOException {
myLogger.setLevel(Level.INFO);

// Crear un manejador de dispositvo de logging.
FileHandler fileTxt = new FileHandler("Anotaciones.txt");

// Crear un formateador de tipo SimpleFormatter
SimpleFormatter formatterTxt = new SimpleFormatter();
fileTxt.setFormatter(formatterTxt);
myLogger.addHandler(fileTxt);

myLogger.severe("Anotacion Severe");
myLogger.warning("Anotacion Warning");
myLogger.info("Anotacion Info");
myLogger.finest("Anotacion Finest");

myLogger.info("------------ Cambio de Nivel ------");

myLogger.setLevel(Level.SEVERE);

myLogger.severe("Anotacion Severe");
myLogger.warning("Anotacion Warning");
myLogger.info("Anotacion Info");
myLogger.finest("Anotacion Finest");
}

}



Y la correspondiente salida obtenida:

01-ene-2012 13:43:54 com.blog.TestingLogger main
GRAVE: Anotacion Severe
01-ene-2012 13:43:54 com.blog.TestingLogger main
ADVERTENCIA: Anotacion Warning
01-ene-2012 13:43:54 com.blog.TestingLogger main
INFO: Anotacion Info
01-ene-2012 13:43:54 com.blog.TestingLogger main
INFO: ------------ Cambio de Nivel ------
01-ene-2012 13:43:54 com.blog.TestingLogger main
GRAVE: Anotacion Severe

Observamos como afecta el cambio de nivel de logging en los mensajes obtenidos en el fichero Anotaciones.txt

domingo, 26 de febrero de 2012

Hola Mundo, Hola Universo

En general cuando empezamos a escribir nuestro código, o simplemente para probar que nuestro entorno de desarrollo funciona correctamente se suele empezar con el típico "Hola Mundo".

Simplemente se teclea:

System.out.println ( "Hola Mundo" );

En esta ocasión quería ir un poco más allá, comenzando a utilizar el paquete java.util.logging de forma que usemos en adelante una forma estructurada de sacar mensajes, por consola o por fichero.

Para complicarlo un poco vamos a sacar dos mensajes, uno con nivel INFO, esto es nivel de información, y otro con nivel SEVERE, es decir incidencia grave. Con nivel INFO sacaremos el mensaje "Hola Mundo" y con nivel SEVERE sacaremos el mensaje "Hola Universo"


import java.io.IOException;
import java.util.logging.Logger;

public class HolaMundo {

private final static Logger MyLogger= Logger.getLogger( HolaMundo.class.getName() );

public static void main (String [] args) throws SecurityException, IOException{

MyLogger.info("Hola Mundo");
MyLogger.severe("Hola Universo");
}
}



Y tenemos como salida:

26-feb-2012 22:56:55 com.blog.HolaMundo main
INFO: Hola Mundo
26-feb-2012 22:56:55 com.blog.HolaMundo main
GRAVE: Hola Universo


En proximos artículos entrare en más detalles de java.util.logging.

Creando un USB con Ubuntu

Hoy tuve un nuevo reto por delante, arrancar en mi netbook linux (ubuntu en este caso) desde el usb.

Para ello lo primero es lo primero, descargar ubuntu

http://www.ubuntu.com/download/ubuntu/download

Una vez hecho esto, en mi caso descargué linuxlive USB creator, un pequeño programa que te ayudará a guardar una imagen ISO o un cd en un usb. Para ello descargamos desde

http://www.linuxliveusb.com


Después de instalarlo, arrancamos y tenemos una simple pantalla.



En esta pantalla determinamos en qué unidad se encuentra instalado el usb, donde se localiza la imagen .ISO, y ya estamos listos. Sólo tenemos que pinchar en el rayo que hay abajo y se realiza la instalación.

Así tenemos en un usb nuestra distribución favorita y podemos llevarla allá donde quiera que vayamos.