Entradas etiquetadas ‘java’
Logging con SLF4J
SLF4J (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.
“null”, o una de las maldiciones de Java
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 (
@Nullabley@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).
La otra conferencia: el futuro de Sun “al descubierto”

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
Verdad verdadera: Groovy truth
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"
Inyección de dependencias con @Autowired, @Qualifier y @Resource
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;
Profiling SQL con P6Spy
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:
- 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.jaryspy.propertiesa nuestro classpath. - 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> - 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 archivospy.propertiesen 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:
- SQL Profiler
- P6Spy viewer (plugin para Eclipse)
Google Collections
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)
Accediendo a miembros estáticos de una clase desde una instancia
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...
}
Redefiniendo toString() con ToStringBuilder
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();
}
Cuidado al sobreescribir el método hashCode()
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.


