Archivo por Autor

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.

Usando Hibernate 3.0: Delete ValueObject Where …

22 feb 2007

No todas las implementaciones de persistencia JPA soportan usando EJBQL, el uso UPDATE y DELETE con clausula WHERE, pero Hibernate si es una de ellas. Si bien, recientemente nos llevamos una desagradable sorpresa al comprobar que Hibernate no soportaba la clausula LIMIT, no podemos si no congratularnos de que sinembargo si que dispongamos de esta otra funcionalidad implementada.
A continuacion va un pequeño ejemplo de uso de DELETE con WHERE en lugar de por Id:


String ejbql = "DELETE "+ fooPersistentVOClass.getSimpleName() + " WHERE "+where;
manager.getTransaction().begin();
deletedEntities= manager.createQuery(ejbql).executeUpdate();
manager.getTransaction().commit();

Ojo que executeUpdate() no devuelve necesariamente el numero de columnas borradas en la operacion, devuelve el numero de entidades eliminadas.

Mas informacion en:

http://www.hibernate.org/hib_docs/entitymanager/reference/en/html/batch.html

Tetris 1D

19 feb 2007

Toda la magia del mitico Tetris condensada en una dimension.

http://www.tetris1d.org/

Estandarizando los codigos internos de error

07 feb 2007

Sera deformacion profesional heredada de mi experiencia laboral anterior, pero cuando nos hemos decidido a crear un Java5 enum en nuestras librerias de desarrollo para la estandarizacion de los codigos de error, me he empeñado en reservar/usar los codigos de respuesta del estandard SIP (Session Initiation Protocol). Realmente son un superconjunto de los codigos de respuesta estandard del protocolo HTTP, con los que la mayoria estamos mas que familiarizados, aunque solo sea de navegar en busca del fin de internet (sic).

Basicamente mi idea ha sido reservar todos los codigos de error entre 100 y 699 para codigos compatibles con el estandard SIP:


1xx: Provisional — Respuestas provisionales, de caracter informativo. Se envia un 1xx si se espera que la respuesta final tarde mas de 200ms.
2xx: Success — Para los casos en que todo ha ido bien.
3xx: Redirection — Hacen falta acciones adicionales para completar correctamente la petición.
4xx: Client Error — Errores en capa cliente, o peticiones mal formadas.
5xx: Server Error — Fallos en servidor ante una peticion aparentemente correcta.
6xx: Global Failure — Fallo global.

Obviamente, para un enum que contenga codigos de error, las respuestas informativas o de exito no nos interesan, pero en cualquier caso decidimos reservar esos rangos y no usarlos. Simple cuestion de compatibilidad hacia delante.

Estos codigos no son ningun secreto y estan disponibles en cientos de paginas en internet, pero picarselos en un enum resulta bastante co~azo, la verdad… asi que aqui estan por si alguien quiere reutilizar…


public enum CodigoError{
	//Especial
	/* Copyrighted */
	//0xx - General (Viavansi)
	/* Copyrighted */
	//1xx - Provisional (Standard) - Informativo de progreso - No usar como codigo de error!
	//2xx - Success (Standard) - No usar como codigo de error!
	//3xx - Redirection (Standard)
	SIP_MULTIPLE_CHOICES(300,"Multiple Choices."),//SIP
	SIP_MOVED_PERMANENTLY(301,"Moved Permanently."),//SIP
	SIP_MOVED_TEMPORARILY(302,"Moved Temporarily."),//SIP
	SIP_USE_PROXY(305,"Use Proxy."),//SIP
	SIP_ALTERNATIVE_SERVICE(380,"Alternative Service."),//SIP
	//4xx - Client or Request (Standard)
	SIP_BAD_REQUEST(400,"Bad Request."),//SIP
	SIP_UNAUTHORIZED(401,"Unauthorized. Need to register."),//SIP Proxys should use proxy authorization 407
	SIP_PAYMENT_REQUIRED(402,"Payment Required"),//SIP
	SIP_FORBIDDEN(403,"Forbidden."),//SIP
	SIP_NOT_FOUND(404,"Not Found."),//SIP
	SIP_METHOD_NOT_ALLOWED(405,"Method Not Allowed."),//SIP
	SIP_REQ_NOT_ACCEPTABLE(406,"Not Acceptable."),//SIP
	SIP_PROXY_AUTH_REQ(407,"Proxy Authentication Required."),//SIP
	SIP_REQUEST_TIME_OUT(408,"Request Timeout."),//SIP
	SIP_GONE(410,"Gone: The user existed once, but is not available here any more."),//SIP
	SIP_ENTITY_TOO_LARGE(413,"Request Entity Too Large."),//SIP
	SIP_URI_TOO_LONG(414,"Request-URI Too Long."),//SIP
	SIP_UNSOPPORTED_MEDIA(415,"Unsupported Media Type."),//SIP
	SIP_UNSOPPORTED_URI_SCHEME(416,"Unsupported URI Scheme."),//SIP
	SIP_BAD_EXTENSION(420,"Bad Extension."),//SIP
	SIP_EXTENSION_REQUIRED(421,"Extension Required."),//SIP
	SIP_INTERVAL_TOO_SHORT(423,"Interval Too Brief."),//SIP
	SIP_TEMPORARILY_UNAVAILABLE(480,"Temporarily Unavailable."),//SIP
	SIP_TRANSACTION_NOT_EXISTS(481,"Transaction Does Not Exist."),//SIP
	SIP_LOOP(482,"Loop Detected."),//SIP
	SIP_TOO_MANY_HOPS(483,"Too Many Hops."),//SIP
	SIP_WRONG_ADDRESS(484,"Address Incomplete."),//SIP
	SIP_AMBIGUOUS(485,"Ambiguous."),//SIP
	SIP_BUSY(486,"Busy Here."),//SIP
	SIP_REQUEST_TERMINATED(487,"Request Terminated."),//SIP
	SIP_REQ_NOT_ACCEPTABLE_HERE(488,"Not Acceptable Here."),//SIP
	SIP_REQ_PENDING(491,"Request Pending."),//SIP
	SIP_UNDECIPHERABLE(493,"Undecipherable: Could not decrypt."),//SIP
	//5xx - Server (Standard)
	SIP_SERVER_ERROR(500,"Server Internal Error."),//SIP
	SIP_NOT_IMPLEMENTED_HERE(501,"Not Implemented: The request method is not implemented here"),//SIP
	SIP_BAD_GATEWAY(502,"Bad Gateway."),//SIP
	SIP_SERVICE_UNAVAILABLE(503,"Service Unavailable."),//SIP
	SIP_SERVER_TIME_OUT(504,"Server Time-out."),//SIP
	SIP_VERSION_NOT_SUPPORTED(505,"Version Not Supported:The server does not support this version"),//SIP
	SIP_MESSAGE_TOO_LARGE(513,"Message Too Large."),//SIP
	//6xx - Global (Standard)
	SIP_BUSY_EVERYWHERE(600,"Busy Everywhere."),//SIP
	SIP_DECLINE(603,"Decline."),//SIP
	SIP_NOT_EXISTS(604,"Does Not Exist Anywhere."),//SIP
	SIP_NOT_ACCEPTABLE(606,"Not Acceptable."),//SIP
	//10xx - Base de Datos (Viavansi)
	/* Copyrighted */
	//11xx - XML (Viavansi)
	/* Copyrighted */;

	private final int codigo;
    private final String mensaje;

    CodigoError(int cod, String mensaje) {
        this.codigo = cod;
        this.mensaje = mensaje;
    }

    /**
	 * @return Devuelve el mensaje por defecto.
	 */
	public String getMensaje() {
		return mensaje;
	}

	/**
	 * @return Devuelve el codigo de error asociado.
	 */
	public int getCodigo() {
		return codigo;
	}

    /* (non-Javadoc)
     * @see java.lang.Enum#toString()
     */
    @Override
    public String toString() {
    	return mensaje+"[codigo error:"+codigo+"]";
    }

}

Cuando Hibernate ataca a Postgres…

07 feb 2007

Quizas este post te ayude si estas buscando una solucion para alguna de las siguientes cuestiones:

  • Pasarle comillas a la BBDD atraves de Hibernate
  • Usar palabras reservadas del gestor de BBDD como nombre de columna o tabla en JPA.
  • Tienes una org.postgresql.util.PSQLException
  • Tienes una excepcion: column notation applied to type name, which is not a composite type

Recientemente jugando con JPA contra Postgres nos hemos encontrado con un problema de los que le pueden volver loco a uno. La excepcion que teniamos era:



 org.postgresql.util.PSQLException: ERROR: .user_id column notation applied to type name, which is not a composite type

Es el tipo de mensaje de error que despista y te puede hacer dar vueltas durante horas (o dias) buscando el problema donde no es.
Nosotros estabamos convencidos de que habia algo erroneo en nuestras annotations de las columnas

@Column(name="user_id")

Pero lo que ya nos hacia tirarnos de los pelos era que nos pasaba lo mismo ya hiciesemos la query consultando user_id o email.



 org.postgresql.util.PSQLException: ERROR: .email column notation applied to type name, which is not a composite type

En general nos ocurria con cualesquiera columnas de nuestra tabla user.

Al final dimos con lo que estaba ocurriendo… el problema estaba en la annotation

@Table(name="user")

Os vais a reir… Resulta que user es una palabra reservada de Postgres, como tambien lo es id y algunas otras. Asi que si llamamos user a nuestra tabla, en la annotation necesitamos usar comillas para que el motor de BBDD no se haga un lio.

Y la forma de pasarle a Hibernate las comillas viene explicada aqui:
http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-quotedidentifiers

O sea, que usando la tilde invertida -`- Hibernate nos la traduce a las comillas adecuadas del dialecto adecuado.

Basicamente, nuestro problema se arreglo haciendo:

@Table(name="`user`")

Autoboxing en Java5

29 ene 2007

En un post reciente en Java Lobby (http://www.javalobby.org), destapaban algo a tener en cuenta cuando usamos alegremente el autoboxing de Java5.

Para el que no lo sepa, gracias al autoboxing podemos de forma muy sencilla asignar un tipo primitivo a su Wrapper class, y viceversa. Es decir, podemos, por ejemplo, asignar un boolean a una clase Boolean.
Por ejemplo, hasta Java5 hay que hacer:


//Sin autoboxing
boolean pBool=false;
Boolean wBool=new Boolean(pBool);//boxing
boolean pBool2=wBool.booleanValue();//unboxing

Desde Java5, sin embargo, la cosa se simplifica:


//Con autoboxing
boolean pBool=false;
Boolean wBool=pBool;//autoboxing
boolean pBool2=wBool;//autounboxing

Tremenda simplificacion! …en el codigo. Obviamente esto puede tener consecuencias en el rendimiento de la aplicacion, especialmente si mezclamos tipos primitivos y objetos wrapper en exprensiones aritmeticas…. asi lo advierte Sun en su documentacion. Asi que cuidadin, que hay que usar el autoboxing con precauciones.

Pero ojo!!!, porque las precauciones que debemos tomar van mas alla de las que Sun advierte…
Cual creeis que sera el resultado de la ejecucion del siguiente codigo:

package test.absurdo; 

public class TestAbsurdo {

 /**
 * @param args
 */
 public static void main(String[] args) {
	Integer a = 1, b = 1 ;
	Integer c = 130, d = 130 ;

	System.out.println( a == 1 ) ; // A
	System.out.println( a == b ) ; // B
	System.out.println( c == 130 ) ; // C
	System.out.println( c == d ) ; // D
 }
}

  1. false
    false
    false
    false
  2. true
    true
    true
    true
  3. true
    false
    true
    false
  4. true
    true
    true
    false

Si a mi me ponen esto en un examen de certificacion, yo diria que c)
Pero, si lo ejecutamos veremos que la correcta es la d)

Por que???

Desde luego no parece tener sentido que a==b sea true y c==d sea false.

Bien, parece ser que hay una “optimizacion” no documentada, que provoca que cualquier objeto Integer cuyo valor int se encuentre entre -127 y 127, es ‘cacheado’ como si fuese un int. Es por eso que a==b vale true aunque se trate de objetos distintos, porque son ‘cacheados’ como tipos primitivos.

 Ciertamente, uno no usa mucho ‘==’ para comparar objetos, pero conviene tener estas cosas por ahi en la recamara por si algun dia nos encontramos con un corpontamiento ‘esoterico’ de nuestro codigo…

Programar es divertido, entregar es tu trabajo

25 ene 2007

Un abstract,  en traducion, mas o menos libre, de algo escrito por Chuck Jazdzewski:

  • Nunca dejes de aprender. Nunca dejes de estudiar.
  • La comunicacion es critica. Aprende a expresarte. Oralmente y por escrito. Transmite confianza. Aprende a comunicarte con otros tecnicos, pero tambien con clientes, con jefes,  gente de marketing, con gente que no sabe ni quiere saber el argot tecnico… Aprende a expresarte en ingles, al menos a nivel tecnico.
  • Promete menos de lo que crees poder hacer, y da mas de lo que has prometido.
  • Admite cuando te equivocas. Y hazlo saber, no lo escondas.
  • Si no ha sido testeado, no funciona. Asumir lo contrario es un error.
  • Programar es divertido, pero no es tu trabajo, entregar si lo es. Si hace falta documentar, hazlo, es tu trabajo.

(El blog de Chuck: http://www.removingalldoubt.com/)

Ejecutar JUnit en el orden deseado

25 ene 2007

Necesitaba una solucion rapida para asegurarme del orden de ejecucion de mis unit tests escritos en jUnit 3.8, sin tener que reescribir codigo.

Fue asi que di con esto, que no hace falta q os leais, a no ser, q no os funcione mi truco:

http://ptrthomas.wordpress.com/2006/08/23/watij-and-running-junit-test-methods-in-the-order-that-you-want/

 De los temas que se comentan, yo implemente el “truco” que uno habia sacado de Ruby….es un truco, mas que una solucion, porque no hay ninguna garantia real de que el orden de ejecucion de los test cases sea exactamente el deseado…

…pero el caso es que funciona… al menos en windows xp professional, con eclipse 3.2 y jdk1.5.0_10

Al grano.

El truco consiste en renombrar los test cases como:

testXXNombreMetodo

Donde XX: es un numero de dos cifras indicando el orden relativo del test case respecto a los demas.

Simple y rapido.

PD: Si no ha sido testeado, no funciona.

Liberar un puerto pillado por Tomcat

24 ene 2007

Una tonteria….

Para Windows: 

Quizas os haya pasado que se os queda frito el Eclipse mientras teniais el Tomcat arrancado, y no hay otra forma de arreglarlo que matar el proceso del Eclipse.

Rearrancamos el Eclipse, y al intentar rearrancar el Tomcat nos salta un error de que el puerto esta ya pillado.

 Bien obviamente, la anterior instancia de Tomcat se ha quedado por ahi.  La forma obvia de arreglarlo es ir a Servicios, y intentar parar alli el Tomcat, pero y si no encontramos el ‘bloody’ servicio?

Solucion facil. En %tomcatdir%\bin ejecutar shutdown.bat

Tachan!

El Tomcat ya arrancara sin problemas en Eclipse.

 Lo dicho, una tonteria…

Metafisica tetradimensional

20 ene 2007

Quizas sea que en las maravillosas playas de Queensland, o en los sobrecogedores paisajes del outback, o navegando por la bahia de Sydney, resulta mas perceptible que las cosas tienen una estructura que no es creada por nosotros, los humanos, que las verdades necesitan creadores de verdades, que las cosas existen en el mundo independientemente de nuestras teorias sobre ellas. Quizas sea por eso que en las ultimas decadas la metafisica denostada por los filosofos del siglo XX, ha recobrado fuerza, y encontrado su sitio en las facultades de filosofia australianas.

Un tal Samuel Alexander, escribio un libro titulado: “Space, Time and Deity” (Espacio, Tiempo y Deidad). Un capitulo del libro era: ‘Time is de “mind” of Space’ (El Tiempo es la “mente” del Espacio).