Archivo | 13:08 hrs.

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

Problemas con Windows Vista (I)

03 mar 2007

Tras un par de días tratando volver a ser el mismo de antes, pero con Windows Vista (yo tampoco me lo explico), paso a enumerar una serie de problemas encontrados (he tenido tantos que voy poniendo sólo los que me han escocido más):

  • El cliente de Oracle 9i puede dar problemas. Con el de 10g no he tenido problemas, aunque Vista avisa de que hay incompatibilidades conocidas.
  • El telnet de toda la vida no viene instalado por defecto (!). Para activarlo, en el Panel de Control -> Programas existe una opción “Activar o desactivar las características de Windows”, donde se ve que esta opción, así como otras lógicas  (el cliente FTP de DOS) están desactivadas.
  • Como tengas una impresora, se rompe el servicio de colas y entras en un bucle que sólo se arregla reiniciando. En nuestro caso, teníamos un problema con una HP Laserjet 1010 (algo viejilla) que tenemos en red, ya que no nos dejaba conectarnos con el driver que Vista trae por defecto embebido. Lo arreglamos instalando la impresora en local (virtualmente, no hubo que pinchar el cable), reemplazando los drivers Vista por unos descargados en HP, y después instalando de nuevo la impresora de red.
  • No soy capaz de conectarme a la VPN de VIAVANSI. Si lo arreglo pondré la solución en un segundo post.
  • Un compañero me ha reportado que no ha podido instalarse PostgreSQL en local sobre Vista, aunque no he podido comprobarlo.

En fin, una pequeña paliza…

Un buen motivo para No utilizar Java 6 en producción

02 mar 2007

Llevo algunos meses utilizando Java 6, testeando la estabilidad y de paso comprobando la compatibilidad del código de nuestros proyectos con la nueva versión de la plataforma, y hoy me he encontrado con un extraño BUG que descarta Java 6(versión 1.6.0) para entornos de producción.

El problema es fácilmente reproducible al ejecutar el siguiente código, que funciona perfectamente en Java 5 y que lanza una excepción de java.lang.ClassNotFoundException: [Ljava.lang.String; al ser ejecutada en Java6:


public class TestJava6BugClassLoader extends TestCase {
      public void testTest() {
          String[] s = new String[] { “Viavansi” };
          String clName = s.getClass().getName();
          try {
              TestJava6.class.getClassLoader().loadClass(clName);
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
     }
}

El problema esta provocado por una modificación en el funcionamiento del ClassLoader, que hace que en determidanas ocasiones( Ej: al recuperar un Array) el método ClassLoader.forName no localice la clase. La recomendación de Sun es que se utilice el método Class.forName() en vez del ClassLoader.loadClass(). El bug ya ha sido reportado( ids:6446627 , 6466061 , 6500212 ,6434149 ), esta en estudio, y probablemente sea solucionado en la próxima versión de la plataforma.
Por otro lado, los principales proyectos están adaptando su código al nuevo comportamiento del cargador de clases.

En resumen, mientras este bug no este solucionado o hasta que todas las librerías que utilicemos tengan en cuenta la modificación en el comportamiento del cargador de clases. no es recomendable utilizar Java6 para entornos de Producción.

Indico la excepción concreta que me ha llevado a encontrar este problema, por si a alguien mas le es de utilidad, como se puede ver es un bug( o comportamiento extraño) que afecta a todo el proceso de serialización/deserialización.


java.lang.ClassNotFoundException: [Lorg.drools.rule.Declaration;
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
	at org.drools.rule.PackageCompilationData$PackageClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
	at org.drools.common.ObjectInputStreamWithLoader.resolveClass(Unknown Source)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
	at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1624)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1323)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1945)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1869)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at java.util.HashMap.readObject(HashMap.java:1029)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1846)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at org.drools.rule.PackageCompilationData.readExternal(Unknown Source)
	at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1792)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at org.drools.rule.Package.readExternal(Unknown Source)
	at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1792)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at java.util.HashMap.readObject(HashMap.java:1029)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1846)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at org.drools.common.AbstractRuleBase.doReadExternal(Unknown Source)
	at org.drools.reteoo.ReteooRuleBase.readExternal(Unknown Source)
	at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1792)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1945)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1869)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at net.sf.ehcache.store.DiskStore.loadElementFromDiskElement(DiskStore.java:283)
	at net.sf.ehcache.store.DiskStore.get(DiskStore.java:238)
	at net.sf.ehcache.Cache.searchInDiskStore(Cache.java:845)
	at net.sf.ehcache.Cache.get(Cache.java:656)
	at net.sf.ehcache.Cache.get(Cache.java:631)
	at com.viavansi.motorreglas.repositorio.simple.RepositorioReglasSimpleImp.getRuleBase(RepositorioReglasSimpleImp.java:112)
	at com.viavansi.motorreglas.repositorio.TestRepositorioSimple.testRepositorioCache(TestRepositorioSimple.java:80)

El Universo: 1 millón de formas de morir, 1 sola de vivir (1ª parte)

01 mar 2007

El Universo es hostil a la vida. Al menos eso nos ha enseñado nuestra ciencia. Cuando Dios diseñó el Universo no lo hizo pensando en que debía albergar vida. Y con Dios me refiero a cualquier manifestación posible del ente creador, pues tanto la ciencia como la religión parten de la misma premisa: de la nada surgió el todo. Unos lo llaman Dios, otros Big Bang. Dos formas de contar lo mismo.

Retomando el tema, el Universo resulta un lugar oscuro, frío y desolado, lleno de rocas inhabitables y sin posibilidad alguna de albergar vida. Nuestra ciencia nos ha demostrado que las circunstancias para que un planeta albergue vida son francamente extraordinarias, hasta tal punto que nos ha llevado a creer que estamos solos en el Universo. ¿Y cuáles son esas circunstancias?

En primer lugar necesitamos una roca habitable, como la Tierra. Un planeta situado a la distancia justa de una estrella para tener una temperatura amigable a la vida. Esa distancia justa depende de la magnitud de la estrella del sistema planetario, el sol en nuestro caso. Si la órbita de la Tierra hubiese sido mínimamente diferente sería otra roca inóspita más. Un planeta infernal o una gélida roca, inhabitable en todo caso. Basta fijarse en nuestro hermano casi gemelo Venus, en cuya superficie el plomo se funde a pesar de estar muy cerca de nosotros, y todo por un devastador efecto invernadero (en otro post hablaré de Venus, que me parece fascinante).

Aún con ello la Tierra no las tuvo todas consigo, pues fue un planeta volcánico hasta que el impacto de un gigantesco meteorito, del cuál nació nuestra Luna, cambio para siempre la faz del planeta. Otra casualidad que de no haberse dado colocaría a la Tierra como otro pedazo de roca en la inmensidad del cosmos.

Esto no basta desde luego. La órbita debe ser completamente helio estacionaria, de modo que no se produzcan oscilaciones abultadas que provoquen cambios bruscos de temperatura. Asimismo la elíptica de la órbita debe ser prácticamente nula por el mismo motivo, evitar la alternancia de períodos glaciales e infernales durante el año.

Pero el sistema planetario también juega su papel, y en esta ocasión el Sistema Solar ha cumplido el suyo. Las observaciones han demostrado que la vida es posible gracias a una configuración muy peculiar de nuestro sistema solar: pequeños planetas rocosos interiores y gigantes gaseosos exteriores. Esta configuración permite que los pequeños rocosos tengan una órbita absolutamente estable, en equilibrio con el Sol y los planetas exteriores. Si los pequeños rocosos fueran exteriores su temperatura sería glacial, y lo más probable es que no tivieran una órbita helio estacionaria y acabaran colapsando contra los gigantes gaseosos.

Pero el papel de estos gigantes van más allá: son un escudo infalible para la supervivencia de los pequeños planetas rocosos. Júpiter es un gigante en nuestro Sistema, que de haber sido un poco más grande hubiera comenzado una reacción en cadena y sería el segundo Sol del Sistema Solar, haciéndolo inhabitable. En su lugar se ha convertido en un coloso de gas cuya tremenda gravedad atrae hacia él (casi) todos los meteoritos que entran en el sistema solar con destino incierto. El último fue muy reciente, en 1996, del que tenemos espectaculares imágenes incluso, y de haber pasado de largo podía haber desintegrado la Tierra.

Con todo y con ello el escudo no es infalible, pues alguna vez hemos recibido el impacto de un meteorito que ha provocado un ELE (Evento de Extinción Masiva en inglés), el último y más popular el que produjo la extinción de los dinosaurios.

Una vez dadas todas estas circunstancias, no hemos acabado. La vida no surge porque sí, hace falta un detonante. Un célebre biólogo dijo que “la probabilidad de que en la Tierra se formara vida es la misma que si un huracán pasara por una ferretería y las piezas al caer ensamblaran un avión”. Tal es el milagro de recombinación que debe producirse y que curiosamente aquí se dió.

Según algunos fue el impacto de un cometa el que trajo todos los componentes esenciales de la vida. Y es que la vida, tal como la conocemos, no puede formarse de cualquier forma. Solo un elemento como el Carbono tiene una red de enlaces suficientemente flexible para desencadenar el milagro, ni siquiera su hermano gemelo el Silicio puede provocar tal efecto.

Una vez dadas todas estas casualidades, es probable (que no seguro) que se haya formado la vida en el planeta. Y una vez la vida se ha formado, ¿qué posibilidades nos quedan de sobrevivir? Lo dejamos para la segunda parte.

Hasta la próxima.

Var args de Java5 en la construccion de APIs

01 mar 2007

Alguien es capaz de predecir la ejecucion del siguiente codigo que usa var args?



public class TestAbsurdo {

      TestAbsurdo(Integer... wis) {
         System.out.println("Constructor Var Args con Integer wrapper.");
      }

      TestAbsurdo(int i, int j) {
         System.out.println("Constructor con dos argumentos int.");

      }

      TestAbsurdo(Integer wi, Integer wj) {
         System.out.println("Constructor con dos argumentos Integer wrapper.");
      }

      public static void main(String... args) {
         System.out.println("Llamamos a TestAbsurdo(2,3)");
         new TestAbsurdo(2, 3);
         System.out.println("Llamamos a TestAbsurdo(new Integer(2),new Integer(3))");
         new TestAbsurdo(new Integer(2), new Integer(3));
      }

   }

Bien, la buena noticia es que de todos los comportamientos posibles, el que parece mas razonable a priori, es realmente el que se da.

Basicamente, de todas las versiones del constructor TestAbsurdo, se usa la que sea mas especifico para la llamada.


Llamamos a TestAbsurdo(2,3)
Version con dos argumentos int.
Llamamos a TestAbsurdo(new Integer(2),new Integer(3))
Version con dos argumentos Integer wrapper.

Por si alguien se lo pregunta, lo siguiente no compila:

 new TestAbsurdo(new Integer(2), 3);

The constructor TestAbsurdo(Integer[]) is ambiguous

Si, sinembargo comentamos las definiciones especificas y dejamos unicamente el constructor con var args:


public class TestAbsurdo {
      TestAbsurdo(Integer... wis) {
         System.out.println("Version Var Args con Integer wrapper.");
      }

      public static void main(String... args) {
         System.out.println("Llamamos a TestAbsurdo(2,3)");
         new TestAbsurdo(2, 3);
         System.out.println("Llamamos a TestAbsurdo(new Integer(2),new Integer(3))");
         new TestAbsurdo(new Integer(2), new Integer(3));
      }
}

El resultado que tenemos es:


Llamamos a TestAbsurdo(2,3)
Version Var Args con Integer wrapper.
Llamamos a TestAbsurdo(new Integer(2),new Integer(3))
Version Var Args con Integer wrapper.

Moraleja: Los var args dan mucha flexibilidad a las APIs, pero, los mismos chicos de Sun, desaconsejan su uso en la sobrecarga de metodos, para evitar este tipo de situaciones, que puede tener desagradables consecuencias.

Por cierto, quizas a alguno le ha llamado la atencion el uso de:


public static void main(String... args)

En lugar del clasico:


public static void main(String[] args)

Ninguna razon especial, ambos son perfectamente validos e intercambiables a partir de Java5. La unica razon por la que he usado la forma var args, es que la primera me parecia mas adecuada en un articulo sobre var args.