sábado, 20 de marzo de 2010

Ordenando listas de POJOs en Java por distintos criterios

Hoy un punto breve y simple para aquellos que siempre tienen dudas sobre como ordenar las colecciones que típicamente nos devuelven frameworks como Hibernate, JPA etc. Aunque muchas veces es mejor dejar que la BDD nos ordene directamente los registros en la consulta, a veces no es posible o por flexibilidad podemos desear hacerlo en el código.

Dicho esto, pasemos al código de ejemplo:

package test;

import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

public class App
{
  static class Item
  {
    private int codigo;
    private String nombre;

    public static Comparator COMPARADOR_POR_NOMBRE = new Comparator()
    {
      private Collator theC = Collator.getInstance();

      @Override
      public int compare(Object o1, Object o2)
      {
        Item item1 = (Item) o1;
        Item item2 = (Item) o2;
        return theC.compare(item1.getNombre(), item2.getNombre());
      }
    };

    public static Comparator COMPARADOR_POR_CODIGO = new Comparator()
    {

      @Override
      public int compare(Object o1, Object o2)
      {
        Item item1 = (Item) o1;
        Item item2 = (Item) o2;
        return item1.getCodigo() - item2.getCodigo();
      }
    };

    public Item(int codigo, String nombre)
    {
      this.codigo = codigo;
      this.nombre = nombre;
    }

    public int getCodigo()
    {
      return codigo;
    }

    public String getNombre()
    {
      return nombre;
    }

    @Override
    public String toString()
    {
      return this.codigo + ": " + this.nombre;
    }

    @Override
    public int hashCode()
    {
      final int prime = 31;
      int result = 1;
      result = prime * result + codigo;
      return result;
    }

    @Override
    public boolean equals(Object obj)
    {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof Item))
        return false;
      Item other = (Item) obj;
      if (codigo != other.codigo)
        return false;
      return true;
    }

  }

  public static void main(String[] args) throws InterruptedException
  {
    List listaDeItems = new LinkedList();
    listaDeItems.add(new Item(2, "Pedro"));
    listaDeItems.add(new Item(1, "Juan"));
    listaDeItems.add(new Item(3, "María"));
    listaDeItems.add(new Item(0, "Paco"));
    System.err.println("Lista tal cual\n-----------------");
    for (Item unItem : listaDeItems)
    {
      System.err.println(".- " + unItem);
    }
    Collections.sort(listaDeItems
                     ,Item.COMPARADOR_POR_NOMBRE);
    System.err.println("Lista ordenada por nombre\n-----------------");
    for (Item unItem : listaDeItems)
    {
      System.err.println(".- " + unItem);
    }
    Collections.sort(listaDeItems
                     ,Item.COMPARADOR_POR_CODIGO);
    System.err.println("Lista ordenada por codigo\n-----------------");
    for (Item unItem : listaDeItems)
    {
      System.err.println(".- " + unItem);
    }
  }
}

Y la respuesta al ejecutar la aplicación sería:

Lista tal cual
-----------------
.- 2: Pedro
.- 1: Juan
.- 3: María
.- 0: Paco
Lista ordenada por nombre
-----------------
.- 1: Juan
.- 3: María
.- 0: Paco
.- 2: Pedro
Lista ordenada por codigo
-----------------
.- 0: Paco
.- 1: Juan
.- 2: Pedro
.- 3: María

El código ya es suficientemente explicativo, y únicamente resaltar el cuidado que hay que tener al implementar los comparadores ya que han de ser coherentes con el método equals, que no hay que olvidar implementar al igual que el método hashCode. Así que si según equals, dos objetos son iguales, el comparador ha de devolver 0. Ojo que esto significa que si esos dos objetos los metemos en una estructura que no permita repetidos, como por ejemplo ordenándolos a través de un SortedSet ,no importa lo que valgan el resto de campos que no se usan en el equals/compare, el objeto anterior desaparecerá. En este caso como usamos una lista durante todo el proceso, podemos meter objetos “repetidos” que no desaparecerán.

¡Felices ordenaciones!

EJ
Happy Coding!

viernes, 19 de marzo de 2010

Gestión simple de módulos y dependencias con java.util.ServiceLoader

En la entrada de hoy vamos a tratar un problema un poco más complejo, y por lo tanto la solución requiere también un poco más de trabajo, pero el resultado final merece la pena.

En este caso el enunciado del problema sería algo tal que así:
  • Supongamos un proceso que puede tener varias implementaciones diferentes y queremos permitir añadir diferentes versiones y permitir escoger una implementación simplemente a través de una cadena de configuración.
La solución que proponemos usa los mecanismos proporcionados en el API básico de Java (se necesita Java 6), sin acudir a soluciones más complejas como OsGI. En caso de necesitar poder actualizar/cambiar las dependencias en tiempo de ejecución y/o tener que poder permitir varias versiones de la misma dependencia a la vez en una misma JVM, sí deberíamos usar algo más complejo, pero si no es necesario, quizá no nos merezca la pena entrar en soluciones tan complejas.

Volviendo a nuestra solución propuesta, la idea es utilizar la clase java.util.ServiceLoader que nos permite declarar y recorrer las clases que implementan una Interfaz dada, pudiendo añadir nuevas implementaciones en el classpath simplemente empaquetando las clases adecuadamente.

El primer punto a tener en cuenta es que podríamos cargar con el ServiceLoader directamente las implementaciones del proceso, sin embargo esta estrategia presenta un problema. ServiceLoader instancia las clases al recorrerlas buscando las implementaciones posibles, así que tenemos que tener toda las dependencias de todas las implementaciones posibles en el classpath o tendremos problemas. En algunos casos eso puede no ser problemático, pero imaginemos que tenemos varias soluciones y que unas usan Hibernate, otras JPA, otras JDO... al cargarlas todas tendríamos que tener todas las dependencias en el classpath, aunque sólo fuéramos a usar JDO, por ejemplo.

Así pues, lo que haremos será que la clase cargada a través de ServiceLoader sea únicamente una utilidad que nos diga el nombre (una cadena) de clase que realmente implemente el proceso. De esta forma podemos cargar la clase utilidad tranquilamente en el classpath sin necesitar cargar todas las dependencias de la implementación.
En la implementación necesitaremos:
  • La interfaz a implementar por todas las clases utilidad. Para hacerlo más flexible permitiremos que una sola clase de utilidad pueda especificar varias implementaciones, cada una con su “clave”:
package my.pckg;

public interface ProcessImplementationRegistration
{
  /*
   Las implementaciones han de devolver una lista de pares clave/implementacion del proceso.
   Por ejemplo: {{“jpa”,”com.my.JPAImplementation”},
                 {“jdo”,”com.my.JDOImplementation”}}
  */
  public String[][] getProcessImplementations();
}
  • En la clase que va a utilizar las implementaciones, cargar las clases que utilizando el mecanismo de la clase java.util.ServiceLoader, y registrar las implementaciones según su clave:
ServiceLoader theServiceLoader = ServiceLoader.load(ProcessImplementationRegistration.class);
    for(ProcessImplementationRegistration pirInstance: theServiceLoader)
    {
      // Usar pirInstance.getProcessImplementations() para registrar
      // las implementaciones por clave
      ...
    }
  • Una vez hecho esto, para añadir una implementación simplemente tenemos que crear la clase que implemente la interfaz my.pckg.ProcessImplementationRegistration, empaquetarla en un jar y añadir en el directorio META-INF del jar, un fichero llamado my.pckg.ProcessImplementationRegistration donde incluiremos el nombre de la clase que implemente la interfaz.
Por ejemplo, clase que implementará la interfaz será:

package com.my;

public class MyJPARegistration implements ProcessImplementationRegistration
{
  /*
   Devolvemos la implementacion que registra este .jar
  */
  @Override
  public String[][] getProcessImplementations()
  {
   return new String[][]{{“jpa”,”com.my.JPAImplementation”}}
  }
}

El fichero META-INF/my.pckg.ProcessImplementationRegistration contendrá únicamente la linea:
com.my.MyJPARegistration
y obviamente en el .jar incluiremos también la clase com.my.JPAImplementation y sus clases auxiliares necesarias si las hubiera.

Colocamos el .jar en el classpath, reiniciamos la maquina virtual Java y voilà: nuestra implementación será descubierta y clasificada según la clave “jpa”. Además, si no la usamos no hará falta que tengamos las clases de JPA en el classpath, por lo que podemos tener los .jar de registro de todas nuestras opciones y sólo necesitaremos tener en el classpath las dependencias de las que realmente usemos.

Este es un mecanismo que utilizan algunos frameworks para incluir plugins opcionales de forma sencilla, basta incluir el fichero .jar en el classpath, sin necesitar incluir las dependencias a no ser que realmente los utilicemos.

Eso es todo. No es una implementación completa pero espero haberos provisto de todos los detalles para que podáis experimentar vosotros mismos. Sólo hace falta tener Java 6, así que animaros a hacer alguna prueba.

Un saludo y hasta la próxima.

jueves, 18 de marzo de 2010

Un poco de magia Groovy para construir clausulas SQL dinámicas

Para empezar, una entrada breve con un truco que aprendí/usé hace poco para construir una sentencia SQL dinámica usando Groovy. El problema reducido es el siguiente:
  • Dada una sentencia SQL que ha de hacer una comparación de una columna y cero, uno o varios valores, construir dinámicamente la sentencia teniendo en cuenta si se le pasa algún valor o varios.
Empezamos con la sentencia, que sería algo tal que así:
SELECT * FROM TAPP_TABLA WHERE TAB_CAMPO='VALOR'
donde podemos pasar cero, uno o varios valores.

La solución típica tiene unos cuantos if/else anidados, concatenaciones de cadenas etc. pero gracias a la mágia de Groovy, tenemos una solución sencilla y directa:

def parametrosMultiples = 'ABE,MUL,TAC,FRO' // o podría ser sólo 'ABE' o null
def condicionOR = parametrosMultiples?.split(',')?.join('\' OR TAB_CAMPO=\'')
def clausulaWhere = parametrosMultiples ? "WHERE TAB_CAMPO='${condicionOR}'" : ''
def sentencia = "SELECT * FROM TAPP_TABLA ${clausulaWhere}" 
println sentencia

Resultado:
SELECT * FROM TAPP_TABLA WHERE TAB_CAMPO='ABE' OR TAB_CAMPO='MUL' OR TAB_CAMPO='TAC' OR TAB_CAMPO='FRO'

Paso a paso:
  • En la primera linea: lo primero que hacemos es aplicar un split a la cadena que nos llega, lo cual la separa en trozos y lo convierte en una lista de valores.
  • En la misma linea: acto seguido hacemos un join de la lista lo cual nos vuelve a juntar todos los valores de la lista y los devuelve concatenados y separados por el separador que le indicamos. El separador está pensado para que enlace bien un valor con el siguiente, y la parte de inicio y fin se la pondremos después.
  • Resaltar que hemos añadido el interrogante ?. a ambas operaciones ya que si no lo hicieramos y el valor de la cadena parametrosMultiples es null, saltaría una NullPointerException.
  • Para evitar añadir la clausula WHERE si no es necesario, usamos el operador de asignación condicional: condición ? valorSiTrue : valorSiFalso.
  • Por último, interpolamos el valor de la variable clausulaWhere al final de la sentencia, haciendo uso de las facilidades de Groovy para introducir valores de variables en cadenas (usando dobles comillas y ${}). 
Veremos que si cambiamos el valor de parametrosMultiples por un valor simple o por null, la cadena devuelta es la correcta y no se produce ningún error.

Bueno, espero que este truquillo os de alguna idea de como poder usar la magia de Groovy para hacer algunas cosas de forma más sencilla.

¡Hasta la próxima!

PD: Resaltar que este truco es válido si nosotros mismos controlamos los valores que puede tomar la variable parametrosMultiples, ya que si no, al estar concatenando cadenas seríamos susceptibles a la Inyección SQL.

    Añadiendo coloreado de sintaxis a tus entradas de blog

    Dado que me ha servido muy bien y como es de bien nacidos ser agradecidos, pongo un enlace a la entrada de blog que me ha ayudado a configurar este blog para poder utilizar sintaxis coloreada en mis ejemplos.

    easy syntax highlighting for blogger

    ¡Gracias Carter!

    Caminante no hay camino, se hace camino al andar.

    Con esta reseña comenzamos la andadura de este blog sobre Java y otros menestares, que espero sea, de vez en cuando, de la utilidad de alguien.

    Aquí van a caerse los truquillos que a veces descubro, las quejas que tengo y todo lo que me parezca. Todo lo reflejado en este blog será mi opinión personal, susceptible de ser equivocada pero mi opinión al fin y al cabo. Y las opiniones son como los culos, todo el mundo tiene uno y piensa que el de los demás apesta :). Así que si no te gusta, puedes razonar para intentar convencerme o puedes despotricar también, sólo que en este caso tu comentario seguramente no aparezca. Esto no es un medio público así que no tengo por que aguantar las chorradas de nadie, si quieres despotricar haz tu propio blog.

    Una vez dicho esto, espero no tener que recurrir mucho a la moderación, que se me cansa la muñeca y no la hinchable precisamente :P.

    ¡Un saludo a todos!