AOWS

Just another adrian’s weblog

Entradas etiquetadas ‘java

Logging con SLF4J

sin comentarios

SLF4JSLF4J (Simple Logging Facade for Java) proporciona una capa de abstracción sobre otros APIs de logging como log4j o LogBack. Esta librería permite cambiar la implementación de forma transparente, de un modo similar a Commons Logging. Sin embargo, ésta última elige el API en tiempo de ejecución (lo cual puede provocar algún que otro problema), mientras que SFL4J lo hace al desplegar la aplicación usando un JAR puente.

Una de las grandes ventajas de SLF4J es que no necesitamos inundar nuestro código con comprobaciones del nivel de log. Si usamos log4j, por ejemplo, podemos tener algo como:

log.debug("Esto es un " + texto + " muy largo, con " + variables[i] + " que se van añadiendo a una " + object.getCadena());

Por mucho que el compilador optimice este código (reemplazando las concatenaciones de cadenas usando StringBuilder), si se ejecuta muchas veces repetidamente sin necesidad (porque la aplicación está desplegada en producción y el nivel de log está por encima de debug, por ejemplo) podría llegar a afectar al rendimiento. Por lo que no es extraño ver código Java tal como este:

if (log.isDebugEnabled()) {
  log.debug("Esto es un " + texto + " muy largo, con " + variables[i] + " que se van añadiendo a una " + object.getCadena());
}

SLF4J nos permite parametrizar el log al estilo printf de C:

log.debug("Esto es un {} muy largo, con {} que se van añadiendo a una {}", texto, variables[i], object.getCadena());

Esto evita problemas de rendimiento al tratar con cadenas, pero evidentemente si los parámetros como object.getCadena() son costosos nada nos librará de tener que usar los dichosos if.

SLF4J tiene más ventajas, para conocerlas nada mejor que este artículo o el manual oficial.

Escrito por adrian

13 Febrero, 2010 a 01:16

Escrito en Uncategorized

Etiquetado con , ,

“null”, o una de las maldiciones de Java

con 5 comentarios

Tratar con valores nulos es, desde siempre, un dolor de muelas muy importante en el mundo de Java. En este artículo de Redcode nos lo recuerdan y proponen algunas de las pocas opciones que hay:

  • Confiar en que nuestras referencias nunca serán nulas y no realizar ninguna comprobación. Como es obvio, un tanto peligroso, sobre todo cuando utilizamos librerías de terceros cuyos resultados no conocemos con seguridad.
    List<Person> people = getPeople();
    for (Person person : people) {
      House house = person.getHouse();
      System.out.println("House price: " + house.getPrice());
    }
    
  • Inundar el código con condicionales anidados, uno detrás de otro según el número de objetos que tengamos.
    List<Person> people = getPeople();
    if (people != null) {
      for (Person person : people) {
        if (person != null) {
          House house = person.getHouse();
          if (house != null) {
            System.out.println("House price: " + house.getPrice());
          }
        }
      }
    }
    
  • Utilizar anotaciones (@Nullable y @NotNull) que nuestro IDE usará para advertirnos de posibles riesgos de obtener un NullPointerException. No nos evitará el tener que hacer comprobaciones, pero podemos fiarnos si no hay warnings.
    @Nullable
    private List<Person> getPeople() {
      // ...
    }
    
    // ... 
    
    List<Person> people = getPeople();
    // recibiremos un warning, y es que "people" puede ser nulo
    for (Person person : people) {
      House house = person.getHouse();
      System.out.println("House price: " + house.getPrice());
    }
    
  • Utilizar algún patrón como Null Object pattern o una versión más general (Special Case) propuesta por Martin Fowler. El problema es que esto no soluciona las interacciones con librerías externas (que pueden seguir devolviendo nulos) y complica el proyecto con un montón de objetos nuevos.

Por desgracia, la nueva versión de Java no incluirá ningún operador específico para solucionar esta situación. Estuvo en los planes incluir algo similar al operador Elvis safe navigation operator de Groovy:

List<Person> people = getPeople()
println people?.get(0)?.getHouse()?.getPrice()

pero finalmente todo seguirá igual. Una pena porque esto mejoraría enormemente la legibilidad del código; lo pondría incluso por delante de nuevas características como las closures (que llegan tarde, esperemos que no mal y arrastro).

Escrito por adrian

4 Febrero, 2010 a 22:47

Escrito en Uncategorized

Etiquetado con ,

La otra conferencia: el futuro de Sun “al descubierto”

sin comentarios

Oracle y Sun
El pasado miércoles, coincidiendo con la presentación del iPad, Oracle ofreció una larga conferencia con su correspondiente sesión de preguntas acerca de la compra de Sun y el futuro de sus productos.

En cuanto a Java, para Oracle la máxima prioridad es sacar la nueva versión cuanto antes (Java 7 saldrá este año), y vendrá con una máquina virtual resultado de la integración entre HotSpot (de Sun) y JRocket (de Oracle / BEA). NetBeans continuará su desarrollo, aunque se centrará en lenguajes dinámicos y scripting; JDeveloper seguirá siendo la principal baza de Oracle para desarrollo Java. Y por último, GlassFish será la “implementación de referencia” de Java EE, lo cual significa que los esfuerzos seguirán centrados en su servidor comercial WebLogic.

¿Y qué pasará con MySQL? Esta base de datos fue un quebradero de cabeza para Oracle en todo este proceso de compra, pues la Unión Europea no veía con buenos ojos este posible monopolio en el terreno de las BD. Oracle parece haber convencido con su compromiso de mejorar MySQL y dedicar recursos al desarrollo de esta BD open source.

Por último, en el apartado de hardware, todo será como se había previsto, y es que Oracle está muy interesada en el combo SPARC / Solaris para ofrecer un stack completo para desarrollo y puesta en producción de aplicaciones.

Visto lo visto, no tengo nada claro que esta adquisición vaya a aportar cosas positivas a los desarrolladores Java, aunque sí es verdad que Sun estaba pasando por un mal momento y necesitaba ayuda urgentemente. El tiempo dirá si Oracle sigue invirtiendo para mejorar la tecnología o en su afán de comercializarla se pierde gran parte de la innovación.

Más info: DZone, javaHispano

Escrito por adrian

29 Enero, 2010 a 21:56

Escrito en Uncategorized

Etiquetado con , , ,

Verdad verdadera: Groovy truth

sin comentarios

El tipo boolean es el único que se puede evaluar a cierto o falso en Java. Algo que por ejemplo no podemos hacer con un String:

if ("esto es un string") {
  // ...
}

Por el contrario, en Groovy todos los tipos tienen su correspondiente verdad:

tipo se evalúa a true
String longitud mayor que cero
Collection colección no vacía
Map mapa no vacío
Number distinto de 0
Iterator si hasNext() devuelve true
Enumeration si hasMoreElements() es true
Matcher si find() retorna true

Esto es cierto para las clases propias de Java / Groovy. ¿Pero qué pasa con las instancias de nuestras clases?

Hasta ahora, las referencias a objetos (de tipo distinto a los anteriores) se evaluaban a true si no eran nulas:

def myNullObject = null
assert !myNullObject

def myNonNullObject = new NonNullObject()
assert myNonNullObject

Pero con la salida de Groovy 1.7 podemos configurar la verdad de cada objeto con el método asBoolean():

class MyRidiculousObject {
  String oneAttr
  Long otherAttr

  boolean asBoolean() {
    oneAttr == "verdad verdadera"
  }
}

assert new MyRidiculousObject(oneAttr: "verdad verdadera", otherAttr: 100)
assert !new MyRidiculousObject(oneAttr: "verdad no tan verdadera", otherAttr: 50)

Y por supuesto cambiar la verdad de otras clases:

String.metaClass.asBoolean = {
  delegate == "verdad verdadera"
}

assert "verdad verdadera"
assert !"verdad no tan verdadera"

Escrito por adrian

9 Enero, 2010 a 17:35

Escrito en Uncategorized

Etiquetado con ,

Inyección de dependencias con @Autowired, @Qualifier y @Resource

con 5 comentarios

Spring proporciona dos anotaciones para la inyección de dependencias: @Autowired y @Qualifier.

@Autowired funciona por tipo, y es que ella sola se encarga de buscar un bean de la clase correspondiente:

@Autowired
private ExampleService exampleService;

La gran limitación de esta anotación es que no es posible hacer inyección por nombre (¿qué pasa si tenemos varios beans del mismo tipo?), por lo que la solución pasa por complementarla con @Qualifier:

@Autowired
@Qualifier("exampleService")
private ExampleService exampleService;

Mientras no llega la versión 3.0 de Spring, una alternativa menos verbosa sería usar la anotación @Resource (perteneciente al JSR-250):

@Resource(name="exampleService")
private ExampleService exampleService;

Escrito por adrian

3 Noviembre, 2009 a 10:25

Escrito en Uncategorized

Etiquetado con ,

Profiling SQL con P6Spy

sin comentarios

P6Spy es una “pequeña” utilidad que nos permite analizar las consultas SQL que lanza una aplicación y su rendimiento. En realidad es un wrapper de nuestro driver JDBC que intercepta y registra las sentencias en un log que posteriormente podemos analizar.

Esto es especialmente útil si queremos ver qué consultas se están generando al utilizar por ejemplo Hibernate o EJB, o cuáles pueden llegar a ser un cuello de botella al poder ver el tiempo total de ejecución de cada una.

Para empezar a usarlo hay que seguir los siguientes pasos:

  1. Instalación: si se realiza a través de Maven, tan fácil como añadir la dependencia:
    <dependency>
        <groupId>p6spy</groupId>
        <artifactId>p6spy</artifactId>
        <version>1.3</version>
    </dependency>
    

    En caso contrario, habría que copiar los archivos p6spy.jar y spy.properties a nuestro classpath.

  2. Configuración del datadsource: una vez tengamos la librería en nuestra aplicación, tenemos que cambiar el datadsource para utilizar el nuevo driver. En este caso es com.p6spy.engine.spy.P6SpyDriver. Con Spring sería algo como:
    <bean id="dataSourceTarget"
          class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>oracle.jdbc.driver.OracleDriver</value>
        </property>
        <property name="url">
            <value>jdbc:oracle:thin:@localhost:1521:xe</value>
        </property>
        <property name="username"><value>test_app</value></property>
        <property name="password"><value>test_app</value></property>
    </bean>
    <bean id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
        <constructor-arg>
            <ref local="dataSourceTarget"/>
        </constructor-arg>
    </bean>
    
  3. Configuración P6Spy: ahora tenemos que indicarle a P6Spy cuál es en realidad el driver que debe utilizar, por ejemplo oracle.jdbc.driver.OracleDriver. Creamos un archivo spy.properties en nuestro classpath donde le indicamos el archivo de log y el driver real de la aplicación:
    module.log=com.p6spy.engine.logging.P6LogFactory
    realdriver=oracle.jdbc.driver.OracleDriver
    logfile=p6spy.log
    

Después de estos 3 pasos probamos a ejecutar de nuevo la aplicación, y veremos cómo se va escribiendo en el archivo de log todas las consultas que se ejecutan. El problema es que este registro es poco legible, por lo que podemos usar alguna de estas herramientas gráficas:

Escrito por adrian

19 Septiembre, 2009 a 11:08

Escrito en Posts

Etiquetado con , , , , ,

Google Collections

sin comentarios

Una de las cosas más irritantes de Java es su API de colecciones. Es verdaderamente frustante tener que usar tal cantidad de verbosidad y ceremonia para simplemente inicializar una lista, y no digamos ya para realizar operaciones más complejas como filtrar los elementos.

Si el cambiar a un lenguaje dinámico no es solución, quizá ayude la librería del buscador gigante Google Collections.

Un par de ejemplos rápidos, primero incializando una lista:

List<Integer> list1 = Lists.newArrayList(1, 2, 3); // nótese que List es de tipo java.util.List
System.out.println(Joiner.on(", ").join(list1)); // imprime "1, 2, 3"

y otro utilizando un filtro:

List<Integer> list = Lists.newArrayList(1, 2, 3, null);
Iterable<Integer> filter = Iterables.filter(list, new Predicate<Integer>() {
   public boolean apply (Integer input) {
      return input == null || (input > 2);
   }
});
System.out.println(Joiner.on(", ").useForNull("100").join(filter)); // imprime 3, 100

Google Collections está disponible en la versión 1.0-rc2 a través de Maven:

<dependency>
    <groupId>com.google.collections</groupId>
    <artifactId>google-collections</artifactId>
    <version>1.0-rc2</version>
</dependency>

o en descarga directa desde su web.

Como vemos no pretende reemplazar a las colecciones nativas de Java, sino que las complementa, las extiende e incluso proporciona alguna nueva.

Enlace | Google Collections
Vídeo presentación | Parte 1 (~40min) y Parte 2 (~53min)

Escrito por adrian

14 Septiembre, 2009 a 22:56

Escrito en Uncategorized

Etiquetado con , ,

Accediendo a miembros estáticos de una clase desde una instancia

con un comentario

En Java es posible acceder a miembros estáticos (ya sean métodos o propiedades) de una clase desde una instancia de la misma (al contrario de lo que ocurre en C# o Ruby, por ejemplo). Nada mejor que mostrarlo en código para verlo:

String cadena = "cadena";
// el método valueOf es un método estático de la clase String
cadena.valueOf("otra cadena");

Esto está permitido, aunque el compilador lanza un warning para advertir de la situación; es algo que debería evitarse y que muchos piensan es un error de diseño de Java.

Lo curioso y algo que puede llevar a malentendidos es que no hace falta que la variable tenga una referencia a una instancia, ya que el acceso al método estático se hace a través del tipo de la variable.

public class Test {

    static String cadena;

    public static void main(String[] args) {
        System.out.println(cadena.valueOf("cadena")); // imprime "cadena"
    }
}

O más chocante (aunque viene a ser lo mismo de arriba):

String cadena = null;
cadena.valueOf("cadena"); // funciona!

Un ejemplo con enumerados (los valores de un enumerado son instancias estáticas del tipo del enumerado):

enum Animals { MONKEY, DONKEY }
Animals a;

void doSomething() {
    System.out.println(a.MONKEY.DONKEY.MONKEY); // y así podríamos seguir...
}

Escrito por adrian

20 Mayo, 2009 a 19:00

Escrito en Uncategorized

Etiquetado con

Redefiniendo toString() con ToStringBuilder

con un comentario

El método toString es más que útil al imprimir logs y/o depurar una aplicación; sin embargo el redefinirlo en cada uno de nuestros objetos puede ser un verdadero coñazo.

Una forma fácil de hacerlo es utilizar la clase ToStringBuilder, de los commons de Apache.

@Override
public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}

Mediante reflexión este método accede a todas las propiedades del objeto y las imprime en un formato que podemos elegir. Si sólo queremos imprimir unos atributos determinados, podemos indicarlo del siguiente modo:

@Override
public String toString() {
    return ToStringBuilder(this).append("name", name).append("age", age).toString();
}

Escrito por adrian

19 Mayo, 2009 a 16:42

Escrito en Uncategorized

Etiquetado con

Cuidado al sobreescribir el método hashCode()

con 4 comentarios

Modificar los métodos equals y hashCode de nuestros objetos no es algo trivial, y puede traer efectos secundarios no muy buenos para nuestra aplicación.

Para qué sirve el código hash

Por defecto, hashCode devuelve un entero diferente para cada objeto basándose en la dirección del mismo (aunque esto depende de la implementación). Su uso principal es la optimización de las colecciones basadas en hashtables, como pueden ser HashMap o HashSet.

De modo gráfico, imaginemos una colección como una serie de cajas. En la caja 1 guardaremos los objetos que tengan hashcode 1, en la caja 2 los que tengan un hashcode 2… y así respectivamente. Cuando busquemos un objeto, sólo tenemos que mirar en la caja donde debería estar (mediante su hashcode).

En esta caja puede haber más objetos; por ejemplo, si sobreescribimos el método hashCode para que devuelva la longitud de un string:

private class CustomObject {
    private String attribute;

    public void setAttribute(String attribute) {
        this.attribute = attribute;
    }

    @Override
    public int hashCode() {
        return this.attribute.length();
    }

}

y tenemos dos objetos, uno con attribute="a" y otro con attribute="b", ambos irán a parar a la caja 1. Por tanto una vez dentro de la caja correspondiente debemos buscar el objeto mediante el método equals.

Esto es más eficiente que recorrer todos los elementos de la colección (siguiendo con el ejemplo anterior, sólo buscaremos en los strings de longitud 1, descartando el resto de elementos nada más empezar) como pasaría en un ArrayList, por ejemplo.

Peligro, ¡cuidado al sobreescribir!

En condiciones normales no deberíamos (ni necesitaremos, seguramente) sobreescribir el método hashCode. El problema viene cuando redefinimos equals (por ejemplo, para ejecutar tests), lo cual implica que también debemos hacer lo mismo con hashCode para mantener la coherencia (dos objetos iguales según equals deben tener el mismo código hash, sino puede ocurrir que por ejemplo tengamos dos instancias idénticas en un conjunto, cuando esto no puede -o no debería- ocurrir).

Dos objetos distintos según equals pueden producir el mismo código hash; por ejemplo:

    @Override
    public int hashCode() {
        return 2;
    }

Este código sería válido pero ineficiente, todos los objetos irían a parar a la misma caja, por lo que las búsquedas posteriores serían secuenciales perdiendo la principal ventaja de usar hashtables.

Otro gran peligro que existe al sobreescribir el método hashCode es el siguiente:

// creamos una colección de tipo HashSet
Set<CustomObject> testSet = new HashSet<CustomObject>();

// creamos un objeto de tipo CustomObject
// y lo añadimos a la colección
CustomObject customObject = new CustomObject();
customObject.setAttribute("attribute");
testSet.add(customObject);

// comprobamos que esté en el conjunto
System.out.println(testSet.contains(customObject)); // true

// modificamos su atributo y comprobamos
// de nuevo que esté en la colección
customObject.setAttribute("another attribute");
System.out.println(testSet.contains(customObject)); // false

Como vemos, al modificar la variable privada de customObject el código hash del objeto cambia (se utiliza en el cómputo), y cuando queremos comprobar si el objeto pertenece a la colección el método contains busca en la caja que no es. El objeto ya no está en la colección.

En definitiva, no tocar

Esto se puede complicar todavía mucho más si ponemos sobre la mesa objetos serializables y variables transient (no debemos utilizar variables de este tipo en el método hashCode, ya que al recuperar el objeto serializado tendrán el valor por defecto).

Este tema es importante y está presente en muchas situaciones, por ejemplo en las entidades de Hibernate (que puede completar los objetos de una colección con valores autogenerados cambiando el hashcode de los mismos y provocando el lío que vimos anteriormente) y los tests.

Lo mejor es, mientras no sea estrictamente necesario, no tocar.

Escrito por adrian

18 Mayo, 2009 a 23:08

Escrito en Uncategorized

Etiquetado con