Archivo por Autor

Java5, uso correcto de enum, un caso real.

19 may 2007

Vale, por una vez me voy a dejar de foos y bars y voy a extraer un pedacito de un codigo que ando ultimamente refactorizando…

En la situacion de partida, tenia un codigo de Java1.4, pero nos hemos migrado a Java5, asi que siempre que puedo toqueteo por aqui y por alla para que el codigo tenga un look&feel java5 real. :)

El fragmento de codigo del que partiamos era el siguiente:


//Recuperamos el tipo de rendicion (MODELO: NORMAL, SIMPLIFICADO, BASICO)
String modelo = request.getParameter("modelo");
if(modelo==null ){
modelo = Constantes.MODELO_RENDICION_NORMAL;
}
if(!modelo.equalsIgnoreCase(Constantes.MODELO_RENDICION_NORMAL) &&!modelo.equalsIgnoreCase(Constantes.MODELO_RENDICION_SIMPLIFICADO)
&& !modelo.equalsIgnoreCase(Constantes.MODELO_RENDICION_BASICO)){
//Trampas
modelo = Constantes.MODELO_RENDICION_NORMAL;
}
log.debug("Tenemos MODELO DE RENDICION: "+modelo);

No creo que requiera mucha explicacion, pero basicamente ese codigo chequea que no nos hacian trampas, y nos llegaba un modelo vacio, o distinto de uno de esos tres: NORMAL, SIMPLIFICADO, BASICO.

Para los que venimos de la escuela del C/C++ encontramos ese codigo, nos resulta, poco menos que realmente horrible por el uso de las constantes (a la manera de #define).

Vale, aqui llego yo de pasada, y apesar de las prisas no puedo evitar meterle mano. Me “cargo” las constantes, y creo un enum…

Las constantes:


//Moved to TipoCuenta enum
public static final String MODELO_RENDICION_NORMAL = TipoCuenta.NORMAL.getModeloRendicion();
public static final String MODELO_RENDICION_SIMPLIFICADO = TipoCuenta.SIMPLIFICADO.getModeloRendicion();
public static final String MODELO_RENDICION_BASICO = TipoCuenta.BASICO.getModeloRendicion();

El enum:


/**
* To distinguish among the different types of accounts avoiding
* the *risky* use of constants
* @author dbejar
*
*/
public enum TipoCuenta {
NORMAL		("Normal","N04","xxxxxx","xxxxxx","xxxxxx"),
SIMPLIFICADO	("Simplificado","S04","xxxxxx","xxxxxx","xxxxxx"),
BASICO		("Basico","B04","xxxxxx","xxxxxx","xxxxxx"),

private String nombreCuenta;
private String modeloRendicion;
private String tipoCuenta;
private String nsCuenta;
private String validacionLiq;

TipoCuenta(String nombreCuenta, String modeloRendicion, String tipoCuenta, String nsCuenta, String validacionLiq){
this.nombreCuenta=nombreCuenta;
this.modeloRendicion=modeloRendicion;
this.tipoCuenta=tipoCuenta;
this.nsCuenta=nsCuenta;
this.validacionLiq=validacionLiq;
}
public String getnombreCuenta(){
return nombreCuenta;
}
public String getModeloRendicion(){
return modeloRendicion;
}
public String getTipoCuenta(){
return tipoCuenta;
}
public String getNSCuenta(){
return nsCuenta;
}
public String getValidacionLiq(){
return validacionLiq;
}
}

Tengo que decir que tengo la mania de codificar en ingles, y que las xxxxxx, son censura :)

Vale, ahora el codigo original seria equivalente a:


//Recuperamos el tipo de rendicion (MODELO: NORMAL, SIMPLIFICADO, BASICO)
String modelo = request.getParameter("modelo");
if(modelo==null){
modelo = TipoCuenta.NORMAL.getModeloRendicion();
}
if(!modelo.equalsIgnoreCase(TipoCuenta.NORMAL.getModeloRendicion()) &&!modelo.equalsIgnoreCase(TipoCuenta.SIMPLIFICADO.getModeloRendicion())
 && !modelo.equalsIgnoreCase(TipoCuenta.BASICO.getModeloRendicion())){
//Trampas
modelo = TipoCuenta.NORMAL.getModeloRendicion();
}
log.debug("Tenemos MODELO DE RENDICION: "+modelo);
rendicion.setModelo(modelo);

Con lo que no estamos haciendo ningun uso real de las ventajas de utilizar enums.

El codigo refactorizado quedo asi:


//Recuperamos el tipo de rendicion (MODELO: NORMAL, SIMPLIFICADO, BASICO)
//Modelo NORMAL por defecto
String modelo = request.getParameter("modelo");
if(modelo==null ){
modelo = TipoCuenta.NORMAL.getModeloRendicion();
}
//This is just to check that we got a valid TipoCuenta
try{
switch (TipoCuenta.valueOf(modelo)){
case BASICO: /*NOOP*/
default: /*NOOP means no operation*/
break;
}
}catch(IllegalArgumentException e){
//Trampas
modelo = TipoCuenta.NORMAL.getModeloRendicion();
}
log.debug("Tenemos MODELO DE RENDICION: "+modelo);

Bueno, en realidad, he añadido lo de case BASICO: para ilustrar mejor a todos los javeros sin experiencia en C la diferencia entre la chapuza de las constantes, y un codigo robusto con enum.

Hala pues,

Realismo modal, las 10 dimensiones, y super cuerdas

09 may 2007

No creo que nunca encuentre tiempo para escribir sobre David Kellogg Lewis, y el Realismo Modal… Muy muy muy brevemente, para Lewis, no es que otros mundos sean posibles, es que, efectivamente, *existen* otros mundos.

El caso es que me he encontrado esta impresionante presentacion en flash, que me parece genial, para entender la teoria de las super cuerdas, la filosofia de Lewis, y obviamente tener una idea mental de las diez dimensiones. Que significa cada dimension, y porque nos quedamos en un numero finito de dimensiones, y, de hecho, porque exactamente 10.

Desgraciadamente para muchos, tiene el audio unicamente en ingles.

http://www.tenthdimension.com/flash2.php

Cuantos axones neuronales usados en conocimiento inutil, lo se.

Hala pues,

Sustituciones masivas con expresiones regulares en eclipse

08 may 2007

Pon que queremos sustituir todos los foo-n donde n es un digito cualquiera, por, por ejemplo: bar_n

Con “Regular expressions” marcado.

En find: foo-([1234567890])
En replace: bar_$1

En este sencillo ejemplo no hubiese hecho falta usar expresiones regulares, pero os puedo asegurar que nos ha ahorrado mucho tiempo en algunas refactorizaciones nominales.

Hala pues,

Ahora me convierto en muerte

07 may 2007

Sabiamos que el mundo no seria el mismo. Unos pocos rieron, unos pocos lloraron, la mayoria estuvo en silencio. Yo recorde una linea del texto Hindu, el Bhagavad-Gita. Vishnu esta tratando de presuadir al Principe de que debe cumplir con su obligacion y para impresionarle toma su forma de multiples brazos y dice, “Ahora, me convierto en Muerte, el destructor de los mundos.” Supongo que todos pensamos eso de una manera u otra.

http://www.atomicarchive.com/Movies/Movie8.shtml

Tree Nation en Agadez, Niger

17 mar 2007


El programa medioambiental de las Naciones Unidas United Nations Environment Programme (UNEP) tiene en marcha una campaña para plantar mil millones de arboles Plant for the Planet: Billion Tree Campaign

Uno de los proyectos mas chulos dentro de esa campaña es el proyecto llamado Tree Nation.

Hay fotos reales del proyecto aqui:

Por cierto que yo ya he plantado mi primer arbol virtual, una Acacia Sieberiana. Que apesar de su nombre, se ve que se da bien en Niger, y ademas incrementa la fertilidad del suelo, algo que es vital por alla.

He dicho virtual, y ha dicho bien, internet es rapido, pero la vida real no tanto, africa aun menos, y los arboles no se plantan instantaneamente, mi arbol tardara un tiempo en tener su representacion en el mundo real, pero no me cabe duda de que lo hara. No solo porque estan las Naciones Unidas detras, si no porque hay gente metida en este proyecto a la que conozco de flickr.com desde hace ya unos añitos.

PS:Vale que tree-nation.com no tiene el diseño Web que nuestros diseñadores aprobarian, ni la arquitectura (PHP5) es la que nos mola por aqui, pero no es eso de lo que va este post.

El valor de Xnoccio.com

15 mar 2007

Una chorraplicacion que me he encontrado por ahi…


Xnoccio.com hoy 15-marzo-2007 vale $564.54.
Cuanto vale tu blog?

Francamente, es mucho mas de lo que me esperaba…
Por 564 dolares americanos yo lo vendia ya :)

OutOfMemory error en Maven2 package

15 mar 2007

Casi me vuelvo loco intentando encontrar la forma de aumentar la memoria que usa mi maven sobre winXP para hacer un package.

Yo ejecutaba normalmente:


mvn  -Dmaven.test.skip=true package

Y obtenia:


[INFO] ------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------
[INFO] Compilation failure
Failure executing javac, but could not parse the error:

The system is out of resources.
Consult the following stack trace for details.
java.lang.OutOfMemoryError: Java heap space

Failure executing javac, but could not parse the error:

The system is out of resources.
Consult the following stack trace for details.
java.lang.OutOfMemoryError: Java heap space

[INFO] ------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------

Bueno, pues despues de intentar de todo, lo unico que he conseguido hacer que funcione es modificar el mvn.bat para que incluya -Xmx512m, que como sabreis significa: “set maximum Java heap size to 512MB” …el problema era hacer que maven2 me lo cogiese.
El sitio donde poner esto en el mvn.bat es donde de establece la variable MAVEN_OPTS.

En mi caso la cosa tiene esta pinta:


%MAVEN_JAVA_EXE% %MAVEN_OPTS% "-Xmx512m" -classpath %CLASSWORLDS_JAR%

Tachan!

Espero que esto ayude a alguien a no perder tanto el tiempo…

Tu primera Annotation chispas (II)

05 mar 2007

Segunda parte de mi serie dedicada a las Annotations.

Ya teniamos nuestra annotation @Foo creada, y la estabamos declarando en el VO. Ahora vamos a intentar usar de alguna manera esos metodos que habiamos dejado anotados.

Pongamos que por ejemplo queremos hacer un toString del VO, pero solo queremos que se nos pinten las propiedades anotadas, y sus atributos.


package test.absurdo;

import test.absurdo.vo.AbsurdoVO;

public class TestAbsurdo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		AbsurdoVO absurdo=new AbsurdoVO();
		absurdo.setBar0(3);
		System.out.println(absurdo);
		System.out.println("fin");
	}
}

La ejecucion de ese codigo evidentemente nos sacara una basura parecida a esta:


test.absurdo.vo.AbsurdoVO@82ba41
fin

Por eso vamos a modificar ligeramente la cabecera de nuestro VO:


package test.absurdo.vo;

import test.absurdo.annotation.Anotado;
import test.absurdo.annotation.chorranotacion.Foo;

public class AbsurdoVO extends Anotado{

[.....]

Y en Anotado vamos a sobreescribir el metodo toString. El codigo es bastante sencillo de entender sabiendo poquito de Reflection pero aun asi, voy a ponerle un poco de literatura…

Basicamente, marco el metodo toString con la annotation @Override, lo cual no es mandatorio, pero no esta de mas.
En #1 recopilo todos los metodos del VO.(getMethods())
En #2, con un foreach de java5, recorro todos y cada uno de estos metodos.
En #3, recupero la annotation de clase Foo del metodo en cuestion, y en #4 y #5 descarto aquellos metodos que no tienen la annotation @Foo.
Las lineas #6 y #7 las he puesto para simplificar el ejemplo; si el metodo tiene parametros tambien lo descarto, solo tendre en cuenta los metodos que esten anotados con @Foo en los getters. Por tanto, por simplicidad, si hubiesemos puesto @Foo en un setter, no lo tendremos en cuenta.

Quizas el quid de todo este en #8, donde añado a un LikedList de Strings lo que quiero que mas tarde sea la salida del toString, y esto es, en mi caso, annotation.alias() que me devuelve el valor del atributo alias de la annotation @Foo. Si @Foo tuviese un atributo llamado patata, la llamada seria annotation.patata(). No es necesaria ninguna comprobacion para recuperar el atributo alias, ya que tiene un valor por defecto, asi que siempre tiene un valor, pero si no tuviese un valor por defecto, y ademas hubiesemos usado la annotation en algun lugar sin darle ningun valor a alias, entonces saltaria un error en tiempo de compilacion. Por tanto, no es necesario comprobar si la llamada annotation.alias() devuelve null o no. Ademas, en #8, tambien llamo al getter m.invoke(this) que me devolvera el valor de la propiedad. Como ya me he asegurado de que el metodo no tiene parametros, no necesito tampoco comprobaciones adicionales aqui. Ademas, le paso solo un parametro, porque como en java 5, invoke esta definido con var args Object java.lang.reflect.Method.invoke (Object obj, Object… args), una caracteristica de los var args, es que no necesito poner ningun parametro, si todos los argumentos variables son null.

Ya despues de esto solo nos queda recorrer la lista de String y preparar la salida de nuestro toString.


package test.absurdo.annotation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import test.absurdo.annotation.chorranotacion.Foo;

public class Anotado {
	@Override
	public String toString(){
		String output = "";
		List descriptores = new LinkedList();
		Method[] metodos = this.getClass().getMethods();//#1
		for (Method m: metodos){//#2
			Foo annotation = m.getAnnotation(Foo.class);//#3
			if (annotation == null) {//#4
				continue;//#5
			}
			if (m.getParameterTypes().length>0){//#6
				//Por simplicidad,
				//@Foo solo se considera en los getters
				//Pasamos de setters y demas...
				continue;//#7
			}
			try {
				descriptores.add(annotation.alias() +
				" "+m.invoke(this));//#8
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {

				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		// si no hay metodos anotados con @Foo,
		//usar el toString por defecto
		if (descriptores.isEmpty()) {
			output=super.toString();
		}else{
			for (String d : descriptores){
				output=output+ d+ ";";
			}
		}
		return output;
	}
}

Y ya esta! Tachan! La salida, de la ejecucion de nuestro main ahora sera mucho mas potita:


Bar de tapas 3;Bar de copas 2;Bar que te clavan 5;Foola 8;
fin

Con esto ya deberiamos ser capaces de hacer y usar nuestras propias annotations.

Se pueden usar las annotations sin tener que extender una clase que haga uso de la annotation? Evidentemente si. Todos los dias usamos annotations de Java5, o Hibernate 3.0, en nuestros VOs, sin tener que hacer ningun extends especifico.

Lo dejo para una tercera parte…

Usando VelocityContext: OutOfMemoryError: Java heap space

04 mar 2007

Ojo con el uso del VelocityContext.
Parece que el recolector de basura no funciona todo lo bien que deberia, al menos en Java5.
Hace ya tiempo tuvimos un problema de consumo excesivo de memoria al reutilizar una List en el mismo objeto VelocityContext
.
Digamos que, por ejemplo, teniamos en una plantilla vm, el siguiente codigo:


#foreach $absurdo in $absurdos
 private String tremendo${absurdo};
#end

Esa plantilla la queriamos usar para distintas cosas pasandole disntinta informacion en $absurdos. Asi que reutilizabamos nuestro objecto VelocityContext, modificandole el valor del contexto absurdos.
Algo como esto:


VelocityContext vcontext=new VelocityContext();
List ls=new LinkedList();
//Puebla ls
for (int j=0; j<900000; j++){
	ls.add("Foo"+j);
}
vcontext.put("absurdos", ls);

//Escribimos el fichero usando la plantilla vm
......
//Y reutilizamos
ls=new LinkedList();
//Puebla ls
for (int j=0; j<900000; j++){
	ls.add("Bar"+j);
}
vcontext.put("absurdos", ls);
//Escribimos un nuevo fichero usando la plantilla vm
......

Bien, pues parece que el recolector de basura no hace su trabajo adecuadamente, al menos de manera automatica….y el consumo de memoria se nos disparaba…

Un pequeño programa cutrecillo para demostrar el problema (Con JDK 1.5.10).


package test.absurdo;

import java.util.LinkedList;
import java.util.List;
import org.apache.velocity.VelocityContext;

public class TestAbsurdo {

	private static final
	String murcielago="El veloz murciélago hindú comía feliz"+
	" cardillo y kiwi.";

	private static long mem0,mem1,mem2;

	public static long usoMem(){
		return Runtime.getRuntime().totalMemory() -
			Runtime.getRuntime().freeMemory();
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		VelocityContext vcontext=new VelocityContext();
		List ls=new LinkedList();
		mem0=usoMem();
		for(int i=1; i<3; i++){
			for (int j=0; j<900000; j++){
				ls.add(murcielago);
			}
			vcontext.put("testabsurdo", ls);
			mem1=usoMem();
			System.out.println("En la iteracion="+i+" mem="+(mem1-mem0));
			ls=new LinkedList();
			mem2=usoMem();
			System.out.println("Despues del new, mem="+(mem2-mem0));
		}
		System.out.println("fin");
	}
}

La salida por consola es:


En la iteracion=1 mem=21403744
Despues del new, mem=21446016
En la iteracion=2 mem=43010320
Despues del new, mem=43010320
fin

Preocupante.

En nuestro caso nos comia la memoria hasta desbordar el heap, y romper la ejecucion.

Curisomente, si forzamos el recolector de basurar, el resultado es muy distinto:


public static void main(String[] args) {
VelocityContext vcontext=new VelocityContext();
List ls=new LinkedList();
mem0=usoMem();
for(int i=1; i<3; i++){
	for (int j=0; j<900000; j++){
		ls.add(murcielago);
	}
	vcontext.put("testabsurdo", ls);
	mem1=usoMem();
	System.out.println("En la iteracion="+i+" mem="+(mem1-mem0));
	ls=new LinkedList();

//FORZAMOS GARBAGE COLLECTION
System.gc();System.gc();System.gc();System.gc();
System.gc();System.gc();System.gc();System.gc();

	mem2=usoMem();
	System.out.println("Despues del new, mem="+(mem2-mem0));
	}
System.out.println("fin");
}

La salida por consola es:


En la iteracion=1 mem=21403744
Despues del new, mem=21402928
En la iteracion=2 mem=43079200
Despues del new, mem=21402928
fin

Lo que demuestra que el problema esta en la gestion del recolector de basuras.

En cualquier caso, como no nos mola nada programar la gestion de la maquina virtual, optamos por otra solucion, menos agresiva. Y aun mas eficaz. Remplazamos las posible llamada al recolector de basura, y recargamos todo lo que necesitamos en el context.


public static void main(String[] args) {
VelocityContext vcontext=new VelocityContext();
List ls=new LinkedList();
mem0=usoMem();
for(int i=1; i<3; i++){
	for (int j=0; j<900000; j++){
		ls.add(murcielago);
	}
	vcontext.put("testabsurdo", ls);
	mem1=usoMem();
	System.out.println("En la iteracion="+i+" mem="+(mem1-mem0));
	ls=new LinkedList();

//RESET VelocityContext
context=null;
context = new VelocityContext();

	mem2=usoMem();
	System.out.println("Despues del new, mem="+(mem2-mem0));
	}
System.out.println("fin");
}

Con salida:


En la iteracion=1 mem=21403744
Despues del new y el reset, mem=21446016
En la iteracion=2 mem=21617248
Despues del new y el reset, mem=21617248
fin

Quizas, lo que debiamos haber hecho desde un principio.

Tu primera Annotation chispas (I)

03 mar 2007

Todo el dia usando las annotations de JPA/Hibernate3 y aun no sabes muy bien de que va?
Como funciona? Como crear tu propia annotation?

La verdad es que crear una annotation es bastante trivial. Quizas el quid de la cuestion sea mas bien, como usar nuestras annotations, como hacer que sean usables.

Como no podria ser de otra manera voy a intentar explicarlo usando ejemplos absurdos. Vale, pongamos que tenemos un VO (value object), que contiene informacion sobre una serie de bares, con sus getters y setters. Digamos que contiene una puntuacion de esos bares. Imaginemos que existe una prestigiosa guia de bares que puntua esos bares. Esa guia se llama Foo -no la conoceis?- y nos mola indicar que bares han sido puntuados por Foo, para eso vamos a usar annotations. Ya he dicho que el ejemplo es absurdo. Muy absurdo.

Empezamos declarando la annotation:


package test.absurdo.annotation.chorranotacion;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
/**
 * @author dbejar
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Foo {
	String alias() default "Foola";
}

Usamos @interface para definir nuestra annotation.
Con @Retention le indicamos la politica de rentencion, RUNTIME significa que la annotation estará disponible en tiempo de ejecucion, de tal forma que podamos usarla por reflection.
Con @Target le indicamos el lugar en el que deben colocarse estas annotations, asi METHOD le indica que esta es una annotation que ira en metodos, no en propiedades. Me llama la atencion la posibilidad de especificar ANNOTATION_TYPE que nos brindaria la posibilidad de añadir mas caracteristicas a nuestra proxima annotation.
Podeis investigar solitos las distintas combinaciones y posibilidades para definir una annotation.

Tambien le hemos añadido un atributo, alias, que sera un String, cuyo valor por defecto es “Foola”. He hecho esto para explicar como añadirle atributos a nuestras annotations caseras. El valor por defecto es opcional, podemos perfectamente añadir atributos sin ningun valor por defecto.

Vale, ya tenemos la annotation declarada. Ahora vamos a usarla en nuestro VO.


package test.absurdo.vo;

import test.absurdo.annotation.chorranotacion.Foo;

public class AbsurdoVO{
	private int bar0;
	private int bar1;
	private int bar2;
	private int bar3;
	private int bar4;
	private int bar5;
	private int bar6;
	private int bar7;
	private int bar8;
	private int bar9;
	public AbsurdoVO(){
		setBar0(0);
		setBar1(1);
		setBar2(2);
		setBar3(3);
		setBar4(4);
		setBar5(5);
		setBar6(6);
		setBar7(7);
		setBar8(8);
		setBar9(9);
	}
	@Foo(alias="Bar de tapas")
	public int getBar0() {
		return bar0;
	}
	public void setBar0(int bar0) {
		this.bar0 = bar0;
	}
	public int getBar1() {
		return bar1;
	}
	public void setBar1(int bar1) {
		this.bar1 = bar1;
	}
	@Foo(alias="Bar de copas")
	public int getBar2() {
		return bar2;
	}
	public void setBar2(int bar2) {
		this.bar2 = bar2;
	}
	public int getBar3() {
		return bar3;
	}
	public void setBar3(int bar3) {
		this.bar3 = bar3;
	}
	public int getBar4() {
		return bar4;
	}
	public void setBar4(int bar4) {
		this.bar4 = bar4;
	}
	@Foo(alias="Bar que te clavan")
	public int getBar5() {
		return bar5;
	}
	public void setBar5(int bar5) {
		this.bar5 = bar5;
	}
	public int getBar6() {
		return bar6;
	}
	public void setBar6(int bar6) {
		this.bar6 = bar6;
	}
	public int getBar7() {
		return bar7;
	}
	public void setBar7(int bar7) {
		this.bar7 = bar7;
	}
	@Foo
	public int getBar8() {
		return bar8;
	}
	public void setBar8(int bar8) {
		this.bar8 = bar8;
	}
	public int getBar9() {
		return bar9;
	}
	public void setBar9(int bar9) {
		this.bar9 = bar9;
	}

}

En la segunda parte explicare como sacarle partido en tiempo de ejecucion a nuestros metodos anotados con @Foo