Nov 20

Sobre constructores, atributos y herencias.

 

Un pequeño "bug" con el que me he tropezado el otro día. Supón una clase padre abstracta en la que desde el constructor se llama al método abstracto.

public abstract ClasePadre {
   public ClasePadre() {
      …
      inicializa();
      …
   }

   public abstract void inicializa() {
   }
}

Ahora imagina que hacemos una clase hija, mal hecha, tal que así

public class ClaseHija extends ClasePadre {
   private UnAtributo atributo = null;

   @Override
   public void inicializa() {
         …
         atributo = new UnAtributo();
         …
      }
   }
}

Simplemente hemos sobreescrito el método inicializa() que nos obliga el padre y lo aprovechamos para inicializar un atributo que inicialmente es null. A partir de aquí, nuestro código se fia de que ese atributo esté inicializado. Pues bien, está mal. Veamos el orden de construcción cuando hacemos new ClaseHija()

  1. Primero java llama al constructor del padre. Este llama a inicializar() y se crea el atributo de la clase hija.
  2. Luego java asigna a los atributos de la clase hija los valores definidos al declararlos, o sea, pone atributo a null.
  3. Finalmente java llama al constructor de la clase hija.

El punto 2 es el que nos da los problemas, resulta que en el punto 1 se inicializa atributo dándole un valor y en el paso 2 se vuelve a poner a null. ¡¡ El atributo queda sin inicializar a pesar de que le hemos hecho un new !!. Nos costó un buen rato dilucidar por qué algo de lo que se hacía el new, un rato después era null.

Pues bien, esto nos ha pasado, y nos ha pasado por pasar de las métricas. Hay una que dice ConstructorCallOverridableMethod, en la que salta un error si un constructor llama a un método que no es final, es decir, que las clases hijas podrían sobreescribir y hacer que la clase padre no quedara bien inicializada. Esto no es exactamente así en este ejemplo, pero está claro que no es buena idea que un constructor llama a métodos que se pueda o, como en este ejemplo, se deban sobreescribir.

Sep 23

Sonar: viene … y se va.

 

Hace un tiempo instalé y probé Sonar, una estupenda herramienta que genera un informe muy vistoso y cómo de usar sobre las métricas de nuestro código, Pero al final tuve que abandonarlo. El compilado con maven generando el informe era muy lento y el servidor web de sonar acababa dando timeout por la carga a la que se veía sometido. Al final el compilado con maven fallaba por este timeout y eso me hacía totalmente imposible integrarlo con hudson, para obtener los informes actualizados todas las noches.

El otro día me dio por revisar cómo iba el desarrollo de Sonar, qué nuevas versiones habían sacado, qué problemas habían resuelto … y me resulto interesante que habían corregido/añadido una nueva "feature", la SONAR-764, en la que básicamente dicen que cargan el trabajo en el plugin de sonar para maven en vez de en el servidor web de sonar. Esto tiene pinta de que puede solucionar los problemas de timeout con el servidor de sonar. Así que a ello, descargar, instalar y probar.

Las primeras pruebas manuales funcionan a la perfección. Eso sí, los compilados tardan casi el doble ya que deben generar además todos los reportes de métricas, pero ya no tengo el problema de timeout y el informe acaba correctamente y se publica en el servidor web de sonar.

Siguiente paso, hacer que Hudson genere con sonar ese informe todas las noches y lo publique. Y ahí empezaron los problemas. Algunos proyectos tardaban más en compilar, pero lo hacían todo bien y el informe se publicaba. Pero otros proyectos, no necesariamente los más grandes, acababan dando una excepción en el compilado, indicando que "Sonar no se puede ejecutar" y un NullPointerException en los MOJOS de maven. Tras unas investigaciones rápidas, no llegué a ninguna conclusión ni ningún arreglo. El mismo proyecto compilado manualmente en el sitio donde lo hace hudson funciona, pero si lo hace hudson no funciona.

Así que mi gozo en un pozo. Desinstalar sonar y esperar a una nueva ocasión.

Apr 16

Métricas: A veces el remedio es peor que la enfermedad

 

Cuando somos novatos y empezamos a codificar, el código no es todo lo bueno que debiera ser, tendemos a hacer clases muy grandes y métodos muy grandes. En algún momento, alguien nos dice que debemos cumplir métricas, nos cuentan que no podemos tener una complejidad ciclomática (igual hasta nos dicen que es eso) mayor que X, que nuestras clases no pueden tener más de Y métodos, etc, etc.

Con buena voluntad, nos ponemos a tratar de cumplir esas métricas, pero las prisas y la comodidad hacen que quizás no las cumplamos a rajatabla. Nuestro código estará un poco mejor, porque nos preocupamos de ellas, pero no las cumplimos al 100%.

Más adelante, algún espabilidado, descubre que algo como maven+pmd puede hacer que si el código no pasa métricas directamente no compile y además pmd tiene un montón de métricas. Así que nuestro código deja de compilar y no nos queda más remedio que arreglarlo y cumplir estrictamente las métricas.

Conseguido, hemos arreglado nuestro código y pasa todas las métricas … y ahí es donde podemos encontrarnos el problema. Todas las métricas salen de algún sitio y están pensadas para que nuestro código sea más legible, tenga menos errores, sea más eficiente, más mantenible, etc. Es fundamental saber qué es lo que hay detrás de cada una de las métricas al intentar hacer/corregir el código para que pase esa métrica. No vale con hacer cualquier cosa para cumplir. Y pongo varios ejemplos:

  • Las clases no pueden tener demasiados métodos ni atributos. ¿Qué hay en el fondo de esta métrica?. En el fondo está que en un buen diseño orientado a objetos cada clase debe tener una y sólo una responsabilidad bien definida. Si una clase tiene muchos métodos y atributos, posiblemente tiene demasiadas responsabilidades y debemos partirla en varias clases, pero podemos hacerlo bien, replanteando el diseño para que esa clase reparta sus responsabilidades adecuadamente en varias, o podemos hacerlo mal, por ejemplo haciendo tres o cuatro clases que heredan unas de otras y repartiendo métodos y atributos entre ellas. Al final sólo instanciamos la más hija de todas, que hereda todos los métodos y atributos de sus padres, el código nos sigue funcionando igual que antes, pasa métricas y tenemos una clase con todo, que es lo que queríamos. Y si resulta que esa clase tiene muchas constantes, pues las metemos en un array de Object y en vez de acceder a la constante por su nombre, accedemos por su índice del array: hemos eliminado 27 constantes por un único arrray con 27 valores. Obviamente, no podemos definir los indices del array en constantes porque nos salta la métrica, así que usamos directamente el índice en nuestro código.
  • La complejidad ciclomática no puede ser muy alta. Obviamente, el objetivo final es que no haya código muy rebuscado y difícil de testear. Un switch-case con muchos case, por simples que sean, no pasa esta métrica. Si queremos hacerlo bien, debemos ver si hay una forma lógica de hacer eso sin un switch-case, como en este código maravilloso, que puede reemplazarse por una sola línea (si acaso dentro de un if). Si el switch-case realmente es inevitable y sencillo, deberíamos saltarnos la regla en este caso concreto. Pero claro, también podemos hacerlo mal, hago el switch-case con tres o cuatro cases y en el default llamo a otro método que a su vez trata otros tres o cuatro cases con otro default que llama a otro método…. Y así hasta que mi clase se pasa de métodos, entonces hago una clase hija con otro método que trata otros tres o cuatro cases…. Imagina el código maravilloso del enlace anterior con el switch-case repartido en varios métodos y unas clases hijas de otras. ¡¡ buff !!.
  • ¿A alguien se le ocurre alguna métrica más y el estropicio adecuado para pasarla?

Al final, tenemos un estupendo código que pasa todas las métricas que debe pasar, pero que es enrevesado como el sólo y no hay quien entienda. Desgraciadamente, la mayoría de los ejemplos que he puesto son reales y los he visto en código.

Nuevamente, el manifiesto ágil, en su principio "Personas sobre procedimientos", tiene toda la razón. Cuando hay programadores no muy expertos, antes de ponerles una herramienta/procedimiento (cumplir métricas o no compila), es mejor darles una formación, contarles las métricas, no lo que significa cada métricas, sino por qué está esa métricas ahí. Hay que enseñarles cómo se corrigen correctamente las más críticas o frecuentes. Es mucho mejor preparar a la gente, que limitarse a la herramienta. De hecho, preparando a la gente y no usando la herramienta saldrá el código mucho mejor que no preparando a la gente y poniendo la herramienta. Quizás no cumpla todas las métricas estrictamente, pero el código será bastante mejor.

Feb 05

Pegas con sonar

 

Como de todos es sabido, soy un "pupas" y las cosas nunca me funcionan bien a la primera ni a la segunda. Comenté hace unos días que había instalado y probado sonar. Las primeras pruebas fueron bastante bien, pero con el uso me he ido encontrando las pegas.

Realmente, pegas no tiene muchas, en realidad, sólo le he visto una, pero molesta bastante.

El plugin de sonar para maven compila nuestro proyecto y pasa todas las métricas. Luego, se pone a insertar en la base de datos de sonar toda esa información. Durante ese tiempo, el servidor web de sonar se queda bastante pillado y responde muy lentamente. El problema es que en ese ordenador tenemos también instalado redmine, archiva y hudson. Todas esas "web" dejan de responder o responden muy mal mientras se están insertando estadísticas en la base de datos.

La siguiente pega, que es más de lo mismo, es que el plugin de sonar para maven también intenta acceder a la web de sonar. No sé el motivo exacto, pero me estoy temiendo que es para avisar al servidor de sonar de que hay nuevas estadísticas en base de datos y empiece e echar unas cuentas, organizar los datos o algo. El caso es que cuando el plugin de maven termina, el servidor sonar puede tirarse diez u once minutos "procesando estadísticas", según pone en la web. Durante ese tiempo, todo el ordenador va muy lento y dejan de responder el hudson, el redmine y el archiva.

Y otra pega más, que viene a ser lo mismo, es que cuando el plugin de maven intenta acceder a la web de sonar, tiene para mi gusto un timeout muy corto, dando error rápidamente si no consigue conectarse. Basta con que pille a Hudson compilando para que aquello tenga ciertas probabilidades de error en la conexión. Y no te digo si le pilla procesando estadísticas del mismo sonar de un proyecto anterior. Ahí es imposible conectarse y hay que esperar. Esta pega es realmente gorda, porque me impide meter el plugin de sonar en el hudson, de forma que además del compilado nocturno, se calculen las métricas nocturnas. El compilado del primer proyecto funciona o no, según le de, pero los demás nunca funcionan.

Total, que voy a tener que agenciarme otro pc/estación de trabajo para instalar exclusivamente sonar. Y aun así, quizás hudson no pueda publicar las estadísticas si pilla a sonar "procesando" información.

Ah, el ordenador no es lo más mejor del mundo, pero tampoco es viejo. Supongo que tendré que probar en un linux en vez de un windows, o quizás es una estación de trabajo solaris.

Feb 01

Acciones adicionales al salvar un fichero con Eclipse

 

Un compañero me mostró una característica interesante de eclipse. Es posible configurarlo para que cada vez que demos "salvar" a uno de nuestros ficheros java, realice en él algunas acciones que le indiquemos: arreglar los imports, darle formato al código, poner las llaves en todos los bloques if, while, for aunque no sean necesarios, eliminar variables locales o privadas que no se usan, etc, etc.

A todo esto se accede desde "window" -> "preferences" -> "java" -> "editor" -> "save actions" (en eclipse versión 3.4.1). Ahí nos muestra una ventana con un montón de acciones que se pueden hacer, junto con un ejemplo de texto java para que veamos cómo afecta.

Hace poco, comenté que he instalado Sonar y viendo algunas de las métricas que tiene en cuenta, veo que muchas pueden cumplirse sin dificultad símplemente configurando eclipse con estas opciones. Por ejemplo, Sonar considera incorrecto (warning) no poner llaves en un if, else o bucle que sólo tenga una línea. Poniendo esta opcion, no solo nuestros nuevos ficheros cumplirán esta métrica, sino que los viejos, según los vayams tocando para lo que sea, se iran arreglando solos.

Un pequeño detalle, ojo con lo de eliminar automáticamente los atributos privados y variable locales no usadas. Yo lo he puesto y según voy escribiendo código, tengo la costumbre de salvar con cierta frecuencia. Si escribo una variable local que voy a usar más adelante, pero todavía no la he usado y le doy a salvar, me la borra, por lo que tengo que volver a escribirla o darle al Ctrl-Z.

Más detalles en Accion adicionales al salvar un fichero con eclipse.

Jan 28

Más de Sonar

Comenté ayer que acababa de instalar Sonar para la medición de métricas en nuestros proyectos. Pues bien, hoy he estado intentando hacer una instalación en serio y me he encontrado con varias pegas.

Lo he instalado en 3 ordenadores. En dos de ellos me ha dado un error curioso al arrancarlo. Busca en la instalación de java un fichero bin/server/jvm.dll. Pues sólo en uno de los ordenadores lo hay. En los otros dos, lo que tengo es un bin/client/jvm.dll. Así que sin ganas de ponerme a investigar sobre la instalación de java, la solución rápida: copie el directorio client con el nombre de server en el mismo sitio y el mismo contenido. Con eso parece que se le engaña para que arranque.

En la documentación de Sonar comenta que viene con la base de datos derby db embebida, pero que es recomendable usar una base de datos más seria. Pues nada, vamos con mysql, que lo tengo instalado. Entro en mysql, creo una base de datos para sonar, voy al fichero de configuración de sonar, quito derby y pongo mysql (esa parte viene muy bien, porque la cadena de conexión de mysql viene comentada y sólo hay que descomentarla y cometar la de derby, además, el driver de mysql en java viene con Sonar, así que ni siquiera hay que poner el jar con el conector en ningún sitio). Más adelante, en el mismo fichero, viene el nombre y usuario para acceso. Y aquí viene otra pega más. Hay que configurar el dialecto de la base de datos, pero en la documentación de Sonar no pone nada de eso. Así que arranco Sonar, abro el navegador, le doy al botón de crear las tablas, empieza a crear las tablas y acaba fallando. Búsqueda del error en google y en el mismo fichero, más adelante hay que indicarle al hibernate de Sonar el dialecto de la base de datos. Dos propiedades más, una con el dialecto mysql y la otra con la clase que sabe de ese dialecto. Eso sí, hay que volver a fijarse porque la clase con el dialecto es propia de Sonar, y no la de hibernate (otro ensayo y error y van dos, en ambos casos con borrado previo de la base de datos, que se había creado a medias).

Listo todo lo referente al servidor. Viene ahora ejecutar el plugin de maven en un proyecto maven para empezar a meter datos.

Mi primera prueba con la base de datos derby funcionó a la primera, así que sin mirar más, le dí al comando. Error. No encuentra la base de datos. Me pongo a buscar por google y resulta que el plugin de maven inserta directamente en la base de datos, por lo que necesita todos los parámetros de conexión. La configuración necesaria para maven viene en la documentación de sonar, así que culpa mía por no haberla leído.  Pongo todo eso, arranco de nuevo…. y otro error. Esta vez porque no conoce el dialecto de la base de datos. La documentación NO pone nada del tema. Así que me lo invento. En el trozo de xml de configuración meto dos tags para el dialecto, exactamente con los mismos nombres que el fichero de configuración del servidor y los mismos valores. Estupendo, ya va.

Me pongo con varios proyectos, poco a poco, y voy consiguiendo los resultados. Pero me salen dos pegas:

  • En algunos proyectos se nos han colado acentos y eñes en los nombres de variables y métodos. Java lo admite. Sonar no. Salta una excepción y falla todo el proceso. Por más que jugué con los encoding, no hubo manera. Al final quité los acentos, pero digo yo que lo suyo es que metiera ese fallo en las métricas en vez de saltar la excepción.
  • En otros proyectos uso una extension wagon-ftp para el deploy. Pues bien, los wagon son incompatibles con los Sonar. El Sonar entra en una llamada recursiva a sí mismo y da una excepción de StackOverflowError. En fin, habrá que buscarle arreglo.

Mañana me toca otro rato de pelea, ya que el plugin de maven no se conforma sólo con insertar directamente en base de datos, sino que accede al servidor Sonar a través de http también para algo y me está fallando esa conexión, supongo que por timeout, o por el proxy corporativo o vete tú a saber por qué.

Hay que reconocer que la herramienta es muy vistosa y que permite ver el estado del proyecto de un vistazo. Pero la instalación y la documentación deja bastante que desear, sobre todo teniendo en cuenta que sólo son cuatro cosas lo que deberían haber puesto, ya que no es una instalación compleja.

Jan 27

Métricas en Java: Sonar

Esta mañana he pasado casi toda la mañana en una reunión de esas de llorar. Se pretendía establecer un plan de acción para solucionar un problema y al final se ha quedado en un "pobrecitos de nosotros…", "cuántos problemas tenemos…", "qué malo es el cliente…", "qué pocos somos para hacer el trabajo…", "qué poco tiempo tenemos …",….

Así que al terminar la reunión, me fuí a comer y me pasé la tarde totalmente desmotivado para el trabajo. Me puse a "internetear" en busca de cosas interesantes y encontré Sonar.

Sonar es una herramienta para la medición de métricas del código java (pmd, javancss, findbugs, … todo junto), pero aporta un par de cosas que me han parecido interesantes.

En primer lugar, al arrancar Sonar, se arranca un servidor web. Las métricas de nuestros proyectos estarán accesibles en el navegador vía web. Bien, de momento nada del otro mundo.

La parte interesante es que las métricas se generan con un plugin de maven y ese plugin busca al servidor web para añadirle el proyecto (si es la primera vez que lo compilamos con el plugin) o para actualizarle las métricas. Esto no deja de ser una pequeña maravilla, puesto que se puede ejecutar el plugin en nuestros compilados nocturnos, de forma que siempre tendremos en el servidor web de Sonar las métricas actualizadas al día. Es más, Sonar guarda las estadísticas de cómo evolucionan las métricas, así que con el tiempo tendremos un gráfico que indica si cada vez hay más o menos violaciones de métricas en el proyecto.

La segunda cosa que me ha resultado interesante es la forma de presentar los resultados. Aparte de las obligadas tablas con valores, se presentan con bloques de colores por proyectos, paquetes y clases. Me explico, hay una vista en la que salen los proyectos como bloques de colores, cada bloque del tamaño proporcional al tamaño del proyecto. El color es rojo si el proyecto tiene muchas violaciones de métricas y va tirando a verde según esté mejor. Pinchando el cuadro de un proyecto, se cambia la vista a los paquetes en ese proyecto, nuevamente con rectángulos de distintos tamaños y colores. Pinchando en uno de esos paquetes, aparecen las clases con el mismo tipo de presentación visual.

Esta forma de presentación es realmente interesante si se pretenden arreglar las métricas de un proyecto que esté mal. Con ella se puede navegar rápidamente e identificar los proyectos, paquetes y clases más conflictivos, centrándose primero en ellos.

Y ya puestos, también tiene una nube de tags en el que cada tag es una de las clases del proyecto. Los tags más gordos son las clases más horribles "métricamente" hablando. Así que incluso es mucho más sencillo identificar de un sólo vistazo cual es la primera clase que deberíamos arreglar de todos los proyectos que tenemos. Por supuesto, te presenta el código fuente de la clase indicando en qué puntos hay violaciones de métricas y cuales.

En este enlace tienes explicadas las distintas pantallas de Sonar y puedes ver los cuadraditos y nubes de tags que menciono.

Actualmente metemos el plugin pmd en maven de forma que si fallan las métricas, falla el compilado. Es la forma de garantizar que la gente codifica siguiendo las métricas desde el principio. Sin embargo, tenemos mucho código con métricas horribles, ya que nunca nos habíamos preocupado en serio de ello. Sonar me va a permitir identificar los peores trozos de código e ir arreglándolos poco a poco.

En fin, Sonar es una herramienta que intentaré ver cómo integro en los proyectos con Hudson (creo que hay plugin de Sonar para Hudson), que me dará entreteniento unos días y si quieren que trabaje en algo serio, que no me metan en reuniones desmoralizantes.