Archive for the ‘Java’ Category

Las estrategias de Oracle para el verano

Lunes, Agosto 16th, 2010

Mediados de agosto suele ser época tranquila para los medios, con los redactores de vacaciones, y los becarios encargándose de las noticias ligeras que ayudan a llenar los espacios. Parece que Oracle está dispuesto a dar movimiento al verano.

Hace algo más de un año de la compra de SUN por parte de Oracle, y ya estamos viendo las primeras consecuencias para el open source:

  • OpenSolaris cancelado: Oracle cancela la iniciativa open source tras OpenSolaris, y pretende reemplazarlo por Solaris 11 Express (gratuito, pero no open source), y finalmente no habrá OpenSolaris 2010.05.
  • Oracle lleva a juicio a Google: el motivo son distintas patentes relacionadas con Java y Android, su sistema operativo para móviles que usa la tecnología de (antes) SUN.

Cuando Oracle adquirió SUN sabíamos que habrían cambios, porque la estrategia de Oracle siempre ha sido muy distinta de la de SUN, mucho más cercano al open source y a una forma más limpia de hacer negocio.

Sin embargo lo que más preocupaba era MySQL, competidor en cierto modo de Oracle, y OpenOffice.org, que no encajaba claramente en el modelo de Oracle.

Parece que todos estábamos equivocados, y es en los productos en los que Oracle tiene realmente interés en los que estamos viendo cambios importantes.

Al menos podemos agradecer a Oracle esta fuente de noticias interesantes para la temporada baja del verano, algunas anotaciones interesantes:

catch (Exception e) {}

Martes, Mayo 4th, 2010

Hola, me gustaría compartir con vosotros un viejo truco de los programadores Java.

Cuando empezamos a hacer algo medio serio con el lenguaje, a la primera compilación nos encontramos con que el compilador nos molesta constantemente en lugares donde hemos invocado métodos de terceros, con mensajes como éste:
unreported exception java.io.IOException; must be caught or declared to be thrown

Esto es muy molesto, porque si se te ocurre lanzar la excepción hacia arriba, luego te encontrás con el mismo mensaje en otro punto del código. Y si la capturas, casi seguro que te molesta con otra excepción diferente, y así hasta no acabar nunca.

Pues aquí está el supertruco:

Cuando quiera que aparezca el molesto mensaje, rodeas la línea, el método entero o bien un conjunto aleatorio de líenas con la expresión
try {

}catch (Exception e) {}

y tus problemas se desvanecen por arte de magia.

Bueno, hasta aquí la ironía. El objeto de este artículo es llamar la atención de esta práctica que no por ser mala se encuentra poco extendida. Incluyendo una enorme cantidad, apostaría a que más del 50% de los códigos en producción.

El problema con el catch (Exception e) {}

¿A qué viene tanta inquina? Bien, el problema es que meter todo el código en este tipo de bloques es subvertir el sistema de excepciones, que, aunque no lo parezca, no se ha creado con objeto de fastidiar.

Lo que se pretende evitar es que, como sucedía con el manejo de errores tradiconal (generalmente a través de devolución de valores por una función), no te olvides de considerar los posibles percances en la ejecución.

Ya no estamos en la facultad, donde te pueden pasar la mano, ni en un programa chusco que te creas para una tarea cotidiana, que si peta lo ejecutas otra vez o ves qué le pasa tranquilamente. Estamos haciendo software que ha de comportarse de forma correcta siempre, y que ha de saber cómo reaccionar ante la más inusitada circunstancia, en vez de dar un batacazo cuando ocurra algo que no se nos haya ocurrido al programar.

El compilador de Java con las excepciones se comporta como una madre o un padre que a vece puede resultar pesado, pero lo hace por tu bien, diciéndote lo que necesitas aunque no quieras oírlo. Es muy difícil que mientras programamos se nos ocurran todas las cosas que pueden ir mal: ¿permisos? ¿espacio en disco? ¿no se pueden resolver nombres? ¿la codificación de caracters no está soportada? ¿el fichero comprimido está corrupto?

El compilador (con la ayuda del que programó la biblioteca que usas) es el amigo que te dice: “¡Eh! ¿Y si la cuenta del usuario ha expirado?”, cuando a ti no se te había ocurrido pensarlo. Lo más inteligente no es ponerse los auriculares e ignorar los avisos.

Manejo responsable de excepciones

Me gustaría daros un criterio mecánico sobre cómo tratar las excepciones. Desgraciadamente, es un arte y debe formar parte del diseño del programa. Dónde recoger y tratar una excepción va a depender íntimamente de la lógica del programa.

Os dejo unas pautas a tener en cuenta:

  • Lo principal es pensar en el problema que ha sucedido y cómo afecta al flujo del programa. Comprobar si se puede solucionar dentro del mismo método (catch), o si hay que dejar la excepción vaya hacia arriba (throws).
  • Hay que ver igualmente, las líneas de código que van invalidadas por la excepción, y asegurarnos de que estén todas dentro del bloque try. Esto puede suponer que reordenemos las líneas de código para que estén juntas las líneas que dependen unas de otras.
  • Hay que poner antención a qué se va a ejecutar tras la excepción, y si sigue teniendo sentido cuando se ha producido el fallo. Hay que mirar tanto las líneas posteriores dentro del mismo método, como las que pertenecen a los métodos donde se llama al que falla.
  • Considerar si hay que deshacer algún cambio ante el fallo (liberar un bloqueo, desconectar del servidor, hacer rollback de una transación, etc)
  • No hacer catchs ni throws genéricos (como recoger Exception), sino recoger las excepciones concretas que puedan producirse. Por mucho que poner 6 catch en un bucle pueda ser pesado.
  • Observar lo útiles que nos son las excepciones para afrontar imprevistos y de qué forma son más útiles, para nosotros a nuestra vez crear excepciones en nuestro código que sean útiles y manejables.
  • No dejar para mañana lo que podamos hacer hoy. Si colocas un try{…}catch (Exception e) {} en el código cuando estás empezando, pensando que ya tratarás los errores más tarde, te encontrarás que después es muy difícil gestionar correctamente las excepciones, pues tu código no estará organizado de una forma propicia.

Puede que tengamos la sensación de que estamos programando enfocados principalmente a las excepciones, pero eso no es malo. Si empezar a aplicar estos principios te causa muchos problemas, significa que estabas haciendo las cosas muy mal (ver último punto).

nullPointerException

Otro problema que puede ser peor son las “excepciones silenciosas”, término que invento sobre la marcha para referirme a aquellas de las que el compilador no se va a quejar. La más habitual es nullPointerException, y nos va a servir para enteder por qué existen. Tan sólo hay que pensar en que cada llamada a un método podría dar esta excepción si la variable sobre la que llama es null.

La nullPointerException no debería atraparse ni gestionarse nunca (o casi nunca). Siempre que ocurre, se debe a un error de la programación.

En vez de atrapar nullPointerException, lo que debemos hacer es pensar en todo momento si las variables tendrán un valor adecuado con seguridad. Si la variable puede ser null (por ejemplo, porque así la devuelve el API que usamos), entonces debemos comprobarlo antes de hacer algo con ella. El qué hagamos en caso de ser null, dependerá también de la lógica del programa, y, de nuevo. El asegurarnos en todo momento de que las variables van a tener un valor correcto, es otra de las cosas que vamos a tener que tener en mente mientras diseñamos y programamos.

Si se nos olvida la comprobación, ya que el compilador no avisa, nos enteraremos generalmente porque el programa cascará con una bonita traza de excepción que nos dirá dónde metimos el gambazo. Si se nos ocurrió rodear el bloque donde ocurre con un try {…} catch (Exception e), no nos enteraremos de que la ejecución se abortó en otro punto, y el problema explotará en otro lugar del código, haciendo difícil rastrear lo que ocurrió.

Ejemplos de mal uso

Supongamos un trozo de código:
Filostro f = null;

try {
Chumba ch = foo.getChumba(); // Llamada que provoca la excepción
f = ch.getFilostro();
} Exception (e) {}

Muy mal. Porque si hay resulta que foo no tiene “chumbas”, f se queda como null, la ejecución se aborta, y nosotros nos hemos quedado tan campantes.

Cuando vayamos a usar f, nos encontraremos un nullPointerException… si es que no hemos rodeado ese bloque con un try{…} catch Exception (e), momento en el cual nos podemos echar a temblar porque las consecuencias pueden ser imprevisibles.

Excepciones a las excepciones

En realidad hay algunos momentos, muy específicos, donde se puede justificar un try{…}catch(Exception e). Supón que vamos a entregar un programa final. Es muy feo que la ventana desaparezca de golpe porque ha ocurrido algún error no contemplado (ej: nullPointerException). Podemos incluir un catch (Exception e) en algún punto muy de alto nivel, que se encargue de loguear el error e informar al usuario de que ha ocurrido un error.

Lo mismo se puede aplicar a páginas webs. Una página tipo “ha ocurrido un error” es fea, pero no tanto como si es tomcat el que se encarga.

Eso sí, insisto en que es *un* punto a muy *alto nivel*, y siempre con mucho control de lo que estamos haciendo. Recuerda que un gatito muere en cada catch (Exception e), por lo que no queremos que ocurra más a menudo de lo necesario.

Terminando

Ha sido un poco árido, pero espero que a partir de ahora me ayudéis a erradicar es mala práctica que tanto nos da que llorar.