Aug 20

Los Web Services y yo

web services Sí, se que ando de vacaciones, pero por suerte para mí, soy de los que todavía les gusta su trabajo y, aunque no estoy trabajando, sí ando "revolviendo" en cosillas que me resultarán útiles más adelante. En concreto, en varios proyectos en los que es posible que participe se usan o se van a usar Web Services. A aprender me toca, sobre todo teniendo en cuenta que es posible que en uno de ellos sea yo el que tome la decisión de qué herramientas/tecnologías usar. Y en ello ando estos días, entre playa y terrazita, leyendo y jugueteando con los Web Services.

Aunque en el proyecto se nos aconseja el uso de .NET, yo soy más partidario de Java, básicamente por tres motivos:

  • Es el lenguaje que conocemos todos los desarrolladores del grupo. De usar .NET, tendríamos un tiempo de aprendizaje mayor: los Web Services y el lenguaje.
  • En Java casi todo es gratis, con el consiguiente ahorro de licencias, tanto de desarrollo como de los servidores.
  • .NET nos limitaría a servidores Windows, mientras que Java nos permitiría desplegar en cualquier servidor, Windows, linux, solaris, …. Sé que está el proyecto MONO, pero no veo la necesidad de meter más complejidad al asunto.

En cualquier caso, no dudo que con .NET se puedan hacer perfectamente Web Services igual que en java, posiblemente más fácilmente integrados con otras cosas de Microsoft, pero no veo ninguna ventaja clara en lo que es estrictamente el lenguaje de uno sobre otro.

El siguiente tema es si SOAP o REST. En principio, por lo que he ido leyendo, me inclino por REST. Aparentemente es más simple y parece ser que es la nueva tendencia. Posiblemente me decida por REST, pero le veo/tengo un par de pegas/dudas.

  • Al ser más nuevo, me da la impresión de que está menos soportado por los servidores/herramientas actuales. Muchas de las herramientas traen como coletilla "soporte para REST". Siempre es un riesgo meterse en algo reciente.
  • De la misma forma, me da la impresión de que toca codificar más. En SOAP creas tu WSDL y aunque no he buscado con detalle, me da la impresión de que hay miles de herramientas que te generan el código tanto de la parte cliente como de la parte servidor para el uso del Web Service definido por ese WSDL. En REST posiblemente haya equivalentes, pero al ser más nuevo, seguro que hay menos o están menos desarrollados. Insisto, no he mirado en serio, es sólo la impresión que me ha dado en una búsqueda superficial.

Otra gran duda es el tipo de librerías a usar para el Web Service. He visto herramientas como Restlet, (es con la que estoy jugando), pero da la impresión de ser algo muy simple para aprendizaje, no tengo muy claro si puede servir para un servidor en producción con fuertes requisitos de seguridad. También hay cosas como JAX-WS o como Jersey, pero el verlos debajo de GlassFish y sobre todo el primero, debajo de Java EE, me da la impresión de que sería matar moscas a cañonazos. Nuestro proyecto sólo tendría unos pocos Web Services y con datos no muy complejos, aunque sí bastante trasiego.

Finalmente, está el tema de elegir servidor, hay cosas como Spring Web Services, Apache Axis 2 o cosas más tradicionales como Jetty, Tomcat o servidores más "bestias" como Glassfish o JBoss. Por un lado, las ganas de aprender me tiran a Spring, pero el irme a algo conocido me tira por Tomcat. Como he comentado, Glassfish o JBoss me parecen excesivos para lo que pretendemos.

En fin, sigo investigando y haciendo pruebas, pero cualquier sugerencia de gente con experiencia por estos lares, es bienvenida.

Jun 30

Curiosidad en java

auto-boxing en javaDe este post del foro, sale un ejemplo curioso de java. Fíjate en el siguiente código

public static void main (String [] args) {
   Integer a = 100;
   Integer b = 100;

   System.out.println(a == b);
}

Pues bien, este ejemplo da como resultado "true". Si cambias los valores de a y b (pero manteniéndolos iguales), verás una cosa curiosa. Para valores entre -128 y 127, el resultado es "true". Para valores fuera de ese rango, el resultado es "false".

El motivo podemos encontrarlo en este post sobre Auto-Boxing del Weblog de Victor Ramirez. Teóricamente las instancias de Integer que se crean de forma más o menos automáticamente en esas asignaciones deberían ser distintas y el == debería dar siempre "false". Pero por eficiencia, el compilador reaprovecha instancias de estas clases, de forma que cuando se hace este tipo de conversión (de un tipo primitivo a un objeto Integer, Boolean, o lo que sea), se tiene que == da "true" en los siguientes casos:

  • Siempre en los Boolean y en los Byte
  • Entre -128 y 127 en los Short e Integer
  • Entre u0000 y u007F en los Character.

Una curiosidad que puede dar muchos quebraderos de cabeza a un programador novato que no tiene muy controlada la diferencia entre el método equals() y el ==.

Jun 21

Mis dudas con Spring para aplicaciones de escritorio

Logo spring frameworkTodos nuestros sistemas se parecen, unos llevan determinados módulos, otros no. Por eso, siempre he estado pensando la forma de hacerlos modulares de forma que sea fácil quitar o poner módulos de un sistema a otro, cada uno con su configuración. Cuando me puse a pensar en ello llegué a la conclusión de que sería buena idea hacer que cada módulo fuera un jar, independiente de los otros. Luego, en un sistema, es cuestión de instanciar y configurar aquellos módulos que nos interesen, trayendo sólo los jar necesarios.

Puesto que los módulos interaccionan entre ellos y cuando uno de ellos quiere hacer algo normalmente necesita datos de otro módulo, o a veces cuando un módulo genera algo necesita avisar a los otros para que sigan el proceso, también me puse a pensar en ello. Una especie de mecanismo de suscripción de eventos, de forma que en algún sitio cerca del main, donde se instancian y configuran módulos, también se hicieran suscripciones a determinados eventos de un módulo y se avisara de ellos a los otros módulos.

Afortunadamente, antes de ponerme a codificar algo como esto, se me ocurrió investigar por internet y me encontré con los contenedores de inversión de control y en concreto con Spring Framework. Aunque el framework está muy pensado para aplicaciones web, está bien diseñado y puedo bajarme y usar sólo el núcleo, lo que me permite a través de ficheros XML instanciar los distintos módulos, configurarlos y hacer que se vean unos a otros. También me proporciona un sistema de publicación y suscripción de eventos entre módulos. Justo lo que quería.

Así que como suelo hacer en estos casos, cogí a un grupo de gente afín a mis ideas peregrinas, les conté lo fácil y bien que podiamos usar Spring Framework en nuestras aplicaciones y lo usamos en uno de los proyectos que empezaban. La experiencia fue buena y todos quedamos bastante contentos en general, así que decidimos ir aplicándolo a más proyectos.

Pero al empezar a usarlo en más proyectos e involucrar a más gente, empecé también a encontrar más oposición y otros puntos de vista. Un compañero mío más crítico que yo con las innovaciones comentó que "Spring no es más que una forma "friky" de hacer news". No obstante, él también andaba buscando una forma de configurar con ficheros sus módulos, ya que se ve repitiendo código de configuración prácticamente igual de un proyecto a otro.

Y el otro día sucedió algo que me ha hecho empezar a dudar de la utilidad de Spring Framework, al menos usando sólo el núcleo y en aplicaciones de escritorio. Este compañero en uno de los proyectos en los que tiene cierta responsabilidad me dijo que se estaban instanciando en Spring dos módulos iguales con configuración distinta, pero que habían cambiado los requisitos y debíamos eliminar uno de ellos y cambiar el otro por un tercero distinto. Nos pusimos a ello.

Borrar uno de los módulos es fácil, sólo hay que ir borrando las referencias en los ficheros XML y en algunos sitios muy concretos del código. Cambiar el otro fue algo más complejo, buscarlo para reemplazarlo, cambiar nombre de clases, parámetros a inicializar, buscar referencias para ver si son compatibles, etc, etc. Una vez que creímos todo estaba hecho, arrancamos la aplicación y nos saltan un par de errores, nos habíamos equivocado en el paquete de una de las clases de un bean de Srping Framework. Más cambios, nueva prueba y nuevo fallo. Esta vez, a la tercera fue la vencida y todo funcionando. Pero mi compañero hizo el comentario que me metió la duda en el cuerpo: "Si en vez de hacer los news y la configuración en ficheros XML los hubieramos hecho en código, todo esto habría cantado en el mismo IDE. Prefiero ver los errores mientras escribo que verlos después arrancando la aplicación".

Y tiene razón, hacer news y configurar en XML tiene la pega de que errores que se verían inmediatamente mientras escribes en el IDE (un new de una clase que no existe por ejemplo, o la llamada a un set para configurar algo) no los vemos hasta que arrancamos la aplicación.

Hay un plugin para eclipse que permite ver si lo que escribes en el XML de Spring es correcto (las clases existen, los métodos existen, etc), pero no lo tenía instalado y lo de que hayan cogido Spring los de SpringSource, me da "mal rollo", parece que se quieren dedicar a cobrar por dar cursos y que este tipo de plugins o incluso las descargas están cada vez más escondidas previo registro.

Instalaré el plugin, miraré la posibilidad de hacer un test de JUnit que verifique que un XML de Spring es correcto y a ver qué pasa. De todas formas, empiezo a pensar que sólo para hacer news no merece la pena. Me queda el tema de los eventos al que sí sacamos partido….

 

Jun 19

Pros y contras de maven

 Llevamos ya varios años usando maven y nos hemos acostumbrado a él. Recordamos ahora cómo teníamos antes los proyectos y nos asombramos de la mejora conseguida. Sin embargo, no todo es bueno con maven, tiene sus pegas.

Ventajas de maven

Hay principalmente tres grandes cosas que hemos conseguido con maven y sin las que ahora no seríamos capaces de trabajar.

La primera es que ahora todos nuestros proyectos están organizados igual. Esto, por supuesto, puede conseguirse con disciplina y sin necesidad de maven, pero en grupos numerosos de desarrolladores es más fácil de conseguir si la herramienta te obliga a seguir una estructura o al menos, si te obliga a hacer un esfuerzo importante si te quieres salir de esa estructura. Se acabó el  hacer scripts de compilado, el preguntar a otro cuando cambias de proyecto dónde están las cosas, el dejar ficheros o iconos por cualquier lado. Ahora cualquiera puede pasar de un proyecto a otro y sabe manejarse por la estructura sin ningún problema.

La segunda gran ventaja son los jar. Al dejar todos los jar de los proyectos en un repositorio centralizado (usamos nexus) y usar versiones SNAPSHOT de maven, todos tenemos siempre disponibles los últimos jars. Antes era necesario que cada uno compilase los jar que  necesitara o se los pidiera a alguien que los tuviera, o que alguien se acordara de meterlos en el sistema de control de versiones. Eso ya no es necesario, maven/nexus se encarga de que todos tengamos siempre los últimos jars.

Y la tercera gran ventaja es la herramienta de integración continua (Hudson), que todas las noches saca los fuentes del sistema de control de versiones, los compila y pone los jars en nexus. Al ser hudson el encargado de meter los jar en nexus, estos siempre están actualizados y siempre está disponible la última versión para todo el mundo. Y al ser hudson el que compila en un servidor separado, eliminamos por un lado los típicos errores de código que sólo compila en el PC del desarrollador Fulanito porque inadvertidamente ha puesto un path suyo local en algún sitio, o se ha olvidado de meter algo en el sistema de control de versiones. Este Hudson nos sirve además para obtener de él las versiones de instalación tanto para el entorno de producción como para pruebas. Ya no dependemos de que alguien tenga todo lo necesario para generar estas versiones y de que ese alguien esté.

Pegas con maven

Pero no todo son ventajas. La gran y enorme pega de maven es su dependencia de que haya internet o al menos red, para acceder al servidor de Hudson. Los problemas con la red suelen ser relativamente frecuentes: se va el servidor de nexus por el motivo que sea, se cae algún proxy o router, etc, etc. En esos casos, se nos queda un poco parado el tema de compilado. Sí, maven tiene una ejecución off-line, pero no funciona todo lo bien que debiera. Si le falta algo, se empeña en ir a buscarlo a través de la red.

Y esta dependencia de la red se nos hace especialmente grave cuando vamos a instalaciones del cliente en los que no hay internet ni, por supuesto, acceso a nuestro nexus. Si queremos llevarnos un entorno de desarrollo para depurar, corregir algún bug y compilar en las instalaciones del cliente, nos obliga a llevarnos toda una copia de repositorios, o montar el proyecto de forma totalmente independiente de maven. Hay comandos de maven que ayudan a hacer toda esta copia, como dependency:go-offline, pero hay que acordarse de hacerlo.

Es bastante molesto también el tema de plugins de maven. A veces y sin saber el motivo, maven decide que debe actualizar plugins a versiones más modernas. En la mayoría de los casos esto no afecta demasiado, pero a veces si coincide con algún tipo de problema de red o el estar off-line, nos hace imposible compilar.

En fin, desde luego las ventajas compensan con creces los inconvenientes y ni se nos pasa por la cabeza dejar de usar maven, pero a veces algunos problemas misteriosos pueden tenerte de pelea con ellos toda una mañana.

May 07

Mirando OSGi

osgi allianceOSGI es una de esas cosas de las que quería enterarme de qué iba y aprovechando el manual de OSGi de Roberto Montero publicado en javahispano, me he puesto a ello.

El manual, para alguien como yo que tiene ciertos conocimientos de java y no le suenan a chino cosas como fichero de manifiesto, classloader o maven, pero que no tiene ni puñetera idea de qué es OSGi, está muy bien. Da una explicación desde el principio y mostrando un ejemplo "Hola Mundo" en diversas plataformas y con distintas herramientas (Equinox, Apache Félix, Eclipse, Pax Constructor, etc). Al final me queda más o menos claro qué es OSGi y sólo queda hacerme mis propios ejemplos y pruebas.

La idea básica de OSGi es que es una forma de desarrollar aplicaciones java a base de hacer módulos más o menos independientes, pero que pueden colaborar entre ellos. Estos módulos (llamados bundles) no son más que jars con algunas clases especiales propias de OSGi y un fichero de manifiesto con campos especiales, que entiende OSGi. Los bundles pueden colaborar entre ellos, indicando qué paquetes java exportan a otros módulos y qué paquetes java de otros módulos necesitan. Una plataforma que implemente OSGi (Equinox o Apache Félix entre otras) es capaz de cargar, arrancar o parar estos módulos en tiempo de ejecución, sin necesidad de tirar la aplicación y volver a arrancarla. También es capaz de saber qué módulos dependen de cuales para pararlos si les faltan dependencias o arrancarlos sólo cuando todas sus dependencias están cargadas.

Sin embargo hay un problema que tengo de hace mucho y que veo que OSGi no me va a solucionar. Cuando hay dos módulos (jars) que se quiere que colaboren, al final no queda más remedio que uno de ellos vea (necesite) al otro para compilar, ya que si un módulo usa otro, debe ver al menos alguna interface o tipo de dato. Esto hace que los módulos no sean realmente independientes y no puedas aprovechar un módulo para otro proyecto. Si en otro proyecto quiero llevarme el móudlo A y este tira de una interface de B, pues me tengo que llevar B también y éste, desgraciadamente, en una de sus clases que no necesito para nada, tira de una interface del módulo C y así sucesivamente. Para proyectos grandes con muchos programadores (expertos y novatos), lo normal es que todos los módulos acaben dependiendo de todos e incluso empiece a haber dependencias cruzadas. Sí, ya sé que "sólo" es cuestión de pensar antes que módulos debe haber, cómo dependen unos de otros y "sólo" hay que conseguir que todos los programadores respeten esa arquitectura.

Una posible solución, que había descartado, para este problema es que cada módulo fuera en realidad dos módulos. Uno pequeño, con sólo las interfaces y clase de datos (beans) que es lo que dependerían los demás módulos. Y otro módulo más grande con todo el resto del código. Lo descarté porque en un proyecto grande con 30 o 40 módulos, se nos duplica el número de módulos y de jars.

Sin embargo, en los ejemplos OSGi que he visto, parece que hace eso mismo. Si crea un módulo que es un servicio que van a ver otros módulos, crea dos módulos: uno con la interface del servicio y los bean de datos, el otro con la implementación. Los módulos que usen servicios de éste, sólo dependen del módulo pequeño con la interface.

En fin, a jugar un poco con OGSi y a repensar esto de partir los módulos en dos submódulos: lo visible al exterior y la implementación concreta.

Apr 28

WeakHashMap y una posible solución de diseño

WeakHashMapHace poco descubrí la clase WeakHashMap de java y encontré un ejemplo de su uso con una idea interesante que no se me había ocurrido y que puede ser una alternativa buena de diseño para resolver determinados problemas. Me "explayo":

Imagina que en tu empresa haces proyectos de software en los que siempre hay listados de personas, pero la información que se guarda de cada persona varía de un proyecto a otro. Por ejemplo, en todos los proyectos las personas tienen DNI, nombre, apellidos, fecha de nacimiento, sexo, etc. Pero en un proyecto que hacéis de un taller de coches la persona tiene además asociado el coche que tiene, con su marca, modelo, etc. En otro proyecto que hacéis de nóminas la persona tiene también su cargo en la empresa, sueldo base, complementos, antigüedad, etc. Y así con cualquier tipo de proyecto que se os ocurra relacionado con personas.

Como somos una empresa espabilada y vemos que siempre tenemos personas en los proyectos y unos datos que siempre son comunes, decidimos hacernos una librería en la que tenemos la clase Persona con los atributos comunes, paneles para editar personas, JTable de personas, daos de persistencia en bd, tablas en bd, etc.

Cuando llega un proyecto concreto debemos ampliar los datos de esa Persona que tenemos en la librería. Y se nos presentan varias opciones.

La que rápidamente piensa cualquiera que haya estudiado orientación a objetos (y quizás sin mucha experiencia), es heredar de Persona y añadir en la clase hija los atributos adicionales. Así, en el ejemplo de nuestro taller, tendríamos PersonaConCoche extends Persona y le añadimos todo lo relativo al coche. Esta solución se puede hacer y funciona, pero al implementarla vemos que no acaba de convencer. El código que hagamos adicional para el taller va a tratar con una Persona a la que hay que hacer cast a PersonaConCoche, debemos rellenar los datos de un mismo objeto en dos consultas de bd e insertarlo en dos veces, unos campos primero y otros después, posiblemente en el orden adecuado, etc, etc.

La siguiente opción que se nos ocurre es poner un método en nuestra clase Persona que sea setDatosAdicionales(datosAdicionales). ¿Pero qué tipo ponemos a esos datosAdicionales en nuestra librería general para que nos sirva en cualquier proyecto?. Pues o bien ponemos Object, o bien hacemos que nuestra clase Persona sea un genérico. La primera opción no es especialmente clara, porque nunca sabremos que hay dentro de datosAdicionales y si somos varios programadores, alguno puede despistarse y meter ahí cualquier cosa. Lo del genérico es un rollo, ya que obliga a casi todas las clases de nuestra librería general (paneles, daos de bd) a ser a su vez genéricos.

Y finalmente está la solución del ejemplo de WeakHashMap que había comentado y que, aunque no he probado, tiene muy buena pinta. Consiste básicamente en meter esos datos adicionales directamente en un HashMap. La clave sería nuestra clase Persona tal cual la tenemos en la librería común y el valor los datos adicionales. Podemos concretar el HashMap genérico para nuestros datos concretos HashMap<Persona, Coche> y así todo queda claro sin posibilidad de confusión. No necesitamos tocar nada de nuestra librería y podemos hacer nuestros paneles y daos de Coche por separado. Por supuesto, este WeakHashMap se guarda fuera de la clase Persona (si no, no hemos mejorado nada respecto a las soluciones anteriores) y hay que hacerlo accesible al que lo necesite.

La ventaja para este tipo de solución de un WeakHashMap frente a un HashMap normal es que el primero no se guarda referencias reales a los objetos que tiene almacenados (ni de Persona ni de Coche), por lo que si en nuestra lista de personas (que tenemos aparte) eliminamos una persona y perdemos todas las referencias a ella, el recolector de basura reclamará también la Persona y sus datos guardados en el WeakHashMap. Si lo hubiéramos metido en un HashMap normal, las referencias a Persona y Coche ahí dentro son reales, por lo que el recolector de basura no los libera. por lo que después de borrar la persona de nuestra lista de personas, debemos además acordarnos de hacer y hacer una llamada al hashmap.remove(persona). Ya sabes que un un grupo de uno o más programadores, siempre hay al menos un despistado que se olvida de estas cosas.

No veo el momento de aplicar una solución de este estilo en algún sitio, a ver si me encuentro con pegas prácticas.

Apr 26

Verlo para creerlo

No tengo muy claro cómo comentar esto para que no sea muy rollo y se pueda apreciar en toda su magnitud, pero voy a intentarlo.

Hoy he estado revisando algo de código que me han pasado (de otra empresa, afortunadamente). Se supone que ese software debe hacer lo siguiente

El software tiene 16 puertos abiertos, todos ellos conectados a equipos iguales, por lo que su mensajería y comportamiento es igual en todos ellos. La idea del software es que el usuario pueda elegir uno de los puertos y hacerle un prueba apretando un botón. La prueba consiste en enviar un mensaje concreto (codificado en el código) y ver si la respuesta tarda menos de un minuto en llegar. Cuando el usuario ha elegido el puerto y pulsa el botón, se abre una ventana con un contador que comienza en 1:00 y va hacia atrás. Si la respuesta llega antes de que termine el contador, se oculta la ventana y se le dice al usuario que el puerto está bien. Si no llega, se cierra igualmente la ventana y se le dice al usuario que el puerto está mal.

¿Y cómo hace esto el software que me han pasado?

Pues se ponen 16 botones en 16 variables boton1, boton2, etc. Se guardan en otra clase 16 atributos estáticos boolean etiquetados testeandoPuerto1, testeandoPuerto2, testeandoPuerto3,…. para indicar qué puerto está bajo test. Por supuesto, los puertos también están guardados en 16 variables estáticas puerto1, puerto2, … Cada botón tiene su ActionListener (16 ActionListener) de forma que cada uno de ellos marca su atributo estático testeandoPuerto correspondiente y abre la ventana con la cuenta atrás.

Por si no fuera poco todo esto, lo bueno empieza ahora. Para la ventana con cuenta atrás se hacen 16 clases Ventana1, Ventana2, etc, una para cada botón. Estas ventanas, en su constructor, miran si se está testeando el puerto que les corresponde para arrancar la cuenta atrás. Y durante la cuenta atrás mira el puerto que le toca a ver si llega la respuesta. Por ejemplo, la Ventana7 en su constructor mirar si testeandoPuerto7 es true para arrancar o no la cuenta atrás. Y si la arranca, en la cuenta atrás se mira puerto7 para ver si llega la respuesta. Es decir, las 16 ventanas son exactamente iguales, salvo la variable testeandoPuerto y puerto, que cada ventana mira las suyas.

Y como todos sabemos que si quieres hacer algo bien es mejor hacerlo uno mismo, para la cuenta atrás no usaremos los Timer de java ni para pintar los minutos:segundos que quedan las clases Date, Calendar, SimpleDateFormat ni similares. Lo mejor es hacerse un hilo "vulgaris", guardándose previamente el currentTimeMillis(), haciendo sleeps de 1000 milisegundos, viendo el tiempo transcurrido respecto al currentTimeMillis() que guardamos al principio y multiplicar/dividir por 1000 milisegundos/segundo, 60 segundos/minuto, cogiendo los restos y las divisiones enteras para separar minutos de segundos.

Y claro, como el arrancar o no el Timer está en el constructor de la ventana y no hemos puesto métodos de arrancar/parar/resetear, pues cada vez que se pulse uno de los botones, se hace new de su ventana correspondiente, haya sido o no creada previamente, ya que si no se hace así no hay forma de rearrancar la cuenta atrás una segunda vez.

Resumiendo, 16 variables botón, con 16 ActionListener, 16 variables estáticas, 16 clases Ventanas exactamente iguales salvo por la variable que miran, y news por doquier.

Si soy bueno, puedo entender que quizás le encargaron esto a alguien que no sabe nada de java, quizás es alguien de perfil hardware que se dedica a testear los equipos con osciloscopios y voltímetros, que a su jefe se le ocurrió la brillante idea de hacer un software que ayude a testear, que a él le ha caído el marrón y que quizás incluso le han apretado en tiempo e hizo el copy-paste de las 16 clases Ventana un Domingo a las cuatro de la madrugada porque el Lunes tenía que estar todo a primera hora.

Pero hay que ver estas cosas para entender realmente lo que se dice en muchos posts y de lo que otros países más avanzados en software que España ya se han dado cuenta. Es mejor pagar el doble, el triple (o incluso 16 veces más) a un programador bueno y con experiencia, que pagar cuatro cuartos a dos o tres sin conocimientos ni experiencia ninguna. Imagino, con los conocimientos de java que demuestra, a la persona que ha hecho este código tardando dos o tres semanas en hacerlo, e imagino a algún programador bueno y con experiencia haciéndolo en una tarde, con 16 veces menos de líneas de código. La pena es que los jefes que contratan o encargan estos "marrones" al primero que pillan, no ven estas cosas, ni posiblemente quieren verlas.

Mar 03

javadoc con UML

Hurgando por ahí me he encontrado con UMLGraph, una herramienta que genera gráficos UML a partir de unos textos que los describen. Lo bueno es que, al menos para el diagrama de clases, el texto que describe el diagrama UML es exactamente igual que un fuente java. Dicho de otra forma, si ponemos en el fichero "class UnaClase extends UnPadre {}", obtendremos un diagrama con una cajita UnaClase que hereda (flechita con triángulo) de una cajita UnPadre. Puedes ver el ejemplo aquí. Esto lo hace ideal para generar los gráficos UML de clases a partir de código fuente ya hecho.

Otro punto interesante es que viene con la posibilidad de invocar javadoc usando UMLGraph, de forma que en nuestro javadoc se generaría diagramas de clases incrustados. UMLGraph viene con un doclet que se puede usar desde el comando javadoc de java y de esta forma, javadoc generará la documentación de la forma habitual, pero incluyendo un gráfico de UML. En la descripción de un package, pondrá todas las clase incluidas en ese package y las relaciones entre ellas. En la descripción de una clase pondrá un dibujo de dicha clase con las relaciones (herencias, dependencias, etc) con otras clases del paquete o de otros paquetes. En la figura puedes ver un ejemplo de javadoc generado con el doclet de UMLGraph.

javadoc con grafico UML

Un detalle a tener en cuenta es que para que UMLGraph pueda generar los ficheros gráficos es necesario tener instalado previamente GraphViz.

En el diagrama de clases podrían configurarse muchas cosas, poner notas asociadas a las clases, poner otro tipo de cajas que no sean de clases, métodos que deben o no mostrarse, etc. La pega de ello es que iría configurado en código a base de anotaciones, por lo que el código quedaría algo "guarreado" para luego ver el dibujo bonito.

También pueden hacerse diagramas de secuencia, pero desgraciadamente la sintaxis del fichero de texto que lo describe ya no es java, así que no deja de ser una forma alternativa de hacer el diagrama. Puede ser interesante, por ejemplo, si guardamos los diagramas de secuencia en un sistema de control de versiones (como subversion). Siempre ocupa menos y es más interesante para ver diferencias con versiones anteriores un fichero de texto que no un gráfico o un proyecto entero de alguna herramienta compleja de generación de gráficos UML (Together, Rational,…).

Y otra cosa que a mí siempre me viene bien, es que UMLGraph está subido al repositorio ibiblio de maven y tiene plugin para el mismo. De esta forma, configurando el fichero pom.xml de nuestro proyecto maven (en concreto, configurando el plugin de javadoc para que use el doclet de UMLGraph), podemos generar el javadoc con gráficos UML directamente desde maven. La configuración sería algo parecido a esto

<reporting>
   <plugins>
      <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
         <configuration>
            <source>1.5</source>
            <aggregate>true</aggregate>
            <doclet>gr.spinellis.umlgraph.doclet.UmlGraphDoc</doclet>
            <docletArtifact>
               <groupId>gr.spinellis</groupId>
               <artifactId>UmlGraph</artifactId>
               <version>4.6</version>
             </docletArtifact>
             <additionalparam>
                    -inferrel -inferdep -quiet -hide java.*
                    -collpackages java.util.* -qualify
                    -postfixpackage -nodefontsize 9
                   -nodefontpackagesize 7
             </additionalparam>
          </configuration>
       </plugin>
    </plugins>
</reporting>

Con esto, un simple mvn javadoc:javadoc nos generaría la documentación javadoc de nuestro proyecto maven, gráficos UML incluidos.

 

Feb 24

OutOfMemoryError con test de maven

 

El otro día me salto un OutOfMemoryError al ejecutarse un test automático desde maven. Teóricamente, para evitar problemas de memoria con maven basta con poner la variable de entorno MAVEN_OPTS con los parámetros que le queremos pasar a la máquina virtual de java, en concreto, los de aumento de memoria

set MAVEN_OPTS=-Xmx512m

De hecho, tengo esa variable puesta por defecto en el entorno y estaba correctamente inicializada. Pero el OutOfMemoryException persiste. Así que a buscar en google.

Al final encuentro que maven arranca una máquina virtual java separada para ejecutar los test y que el parámetro MAVEN_OPTS sólo afecta a la máquina virtual en la que corre maven y no a la máquina virtual en la que se ejecutan los test. El plugin de maven que se encarga de ejecutar los test automáticos se llama maven-surefire-plugin y tiene su propia configuración. La variable argLine permite indicar, entre otras cosas, la cantidad de memoria que queremos que se asigne a la máquina virtual java en la que se ejecutan los test. Para ello, debemos ejecutar así

mvn -DargLine=-Xmx512m test

o bien, configurarlo en el mismo pom.xml del proyecto

<project>
<build>
<plugins>
   …
   <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>               
      <configuration>
         <argLine>-Xmx512m</argLine>
      </configuration>
   </plugin>
 

Feb 22

Más sobre los test automáticos

 

Comenté ayer que estaba jugando con fest-swing y otras herramientas para test automáticos de pruebas de las interfaces gráficas de usuario. Pues bien, hoy he seguido un poco y he conseguido resolver la mayor parte de los problemas que se me presentaron. Eran, por supuesto, culpa mía, de no conocer la herramienta y de la mala costumbre de jugar el ensayo y error en vez de leerse la documentación.

Por un lado, en vez de bajarme fest-swing de la página de fest-swing, usé el repositorio maven que hay para ello, dejando que fuera maven el que se bajara el jar de fest-swing y todas sus dependencias. Entiendo que esto no tiene mucho que ver (más bien nada) con los problemas que se me presentaron.

El segundo punto, y este sí es importante, es que si hacemos el ejemplo tonto de test que viene con fest-swing no hay ningún problema, sale bien a la primera. Pero si ejecutamos una batería de test con fest-swing, es muy importante reiniciar los recursos que usa fest-swing. Esto se traduce que en el método tearDown() (que JUnit ejecuta después de cada test), debemos hacer la llamada correspondiente

private FrameFixture window; // Clase de Fest-Swing con la ventana principal.

public void tearDown() {
   window.cleanUp();  // reiniciar recursos
}

Y esto ha solucionado gran parte de los problemas que tenía. Ahora las ventanas de test sí salen cuando se ejecutan desde maven y no hacen tantas cosas raras cuando se ejecutan desde eclipse.

Otro tema importante es que aunque Fest-Swing se encarga de acceder a los componentes en el hilo de tratamiento de eventos de swing, es posible que el orden de estos eventos no sea el que esperamos. Si nuestro test manda visualizar una ventana, he encontrado útil esperar a que dicha ventana esté realmente visible antes de proseguir con el test. Esto se puede conseguir con la llamada a window.requireVisible() de fest-swing. Esta llamada hace fallar el test si la ventana no está visible, pero espera un tiempo prudencial antes de cantar el fallo. De esta forma, la llamada se queda bloqueada hasta que la ventana realmente está visible, o hasta que pasa un tiempo excesivo de espera (configurable, por supuesto).

Lo que no he conseguido es que desde eclipse los test se ejecuten dando resultados siempre. Sí es cierto que con todo esto parece que falla menos, pero siguen saliéndome barras grises o negras (en vez de verdes o rojas) de vez en cuando.

Una vez conseguido que todo funcione más o menos correctamente, lo he metido en Subversion y he esperado a que el sistema de integración continua (Hudson) compilara y ejecutara los test. A pesar de las advertencias en la documentación de fest-swing de que puede haber problemas con la integración continua, todo ha ido sobre ruedas y sin problemas.

Otra característica que me ha parecido interesante de fest-swing, aunque no la he probado, es que si un test falla, fest-swing puede capturar y almacenar la pantalla en el momento que se produce el fallo. De esta forma, viendo la foto de nuestra interfaz gráfica de usuario con el fallo, podemos ver si realmente hay algo incorrecto o el fallo se ha producido por otra circunstancia. Por ejemplo, que la ventana de que "windows se ha actualizado" haya salido justo encima de nuestra interfaz justo cuando la estamos testeando.

Así que de momento fest-swing queda incorporado en mi trabajo como herramienta para hacer test de interfaces gráficas de usuario. Sólo me falta ir convenciendo a mis "compis" para que también la usen.

Comenté también en el post anterior que quería probar Abbott. Bien, después del éxito con fest-swing, no lo he hecho. Pero leyendo la documentación de Abbott, he visto una característica también interesante. Abbott, ¿cómo no?, viene con Costello, una aplicación que permite ejecutar nuestra interfaz de usuario de forma normal (a través de su método main()) y actuar manualmente sobre ella. Costello se encargará de capturar toda nuestra interacción con ella (a modo de grabación), de forma que luego Costello puede reproducirla una y otra vez sobre nuestra aplicación, testeando que los resultados son los mismos. Es una forma interesante de hacer los test, sin necesidad de programar demasiado. A pesar de que usaremos fest-swing, probaré Abbott y Costello en algún momento, para ver si se puede testear el sistema completo en el entorno de pruebas.