¿ Cómo Interpreto un Mensaje de Error en Java ? Parte 1 de 2 - Interpretando Stack Traces

Copyright © ACSINET S.A. de C.V. 2007
Derechos Reservados de este Material:
Atribución-No Comercial-Licenciamiento Recíproco 2.5 México
http://creativecommons.org/licenses/by-nc-sa/2.5/mx/

Eres libre de: a) copiar, distribuir y comunicar públicamente la obra, b) hacer obras derivadas. Bajo las condiciones siguientes: a) Atribución. Debes reconocer la autoría de la obra en los términos especificados por el propio autor o licenciante. b) No comercial. No puedes utilizar esta obra para fines comerciales. c) Licenciamiento Recíproco. Si alteras, transformas o creas una obra a partir de esta obra, solo podrás distribuir la obra resultante bajo una licencia igual a ésta.
Al reutilizar o distribuir la obra, tiene que dejar bien claro los términos de la licencia de esta obra. Alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor. Nada en esta licencia menoscaba o restringe los derechos morales del autor.
Java and all Java-based trademarks and logos are trademarks of Sun Microsystems in the United States and/or other countries.
Other products and services are trademarks of their respective owners.

Los errores en aplicaciones en lenguaje java se presentan generalmente como Stack Traces. Un ejemplo típico de un stack trace es el siguiente:.

java.sql.SQLException: No such column name
at com.informix.util.IfxErrMsg.getSQLException(IfxErrMsg.java:355)
at com.informix.jdbc.IfxResultSet.findColumn(IfxResultSet.java:632)
at com.informix.jdbc.IfxResultSet.getTimestamp(IfxResultSet.java:1321)
at org.apache.commons.dbcp.DelegatingResultSet.getTimestamp(DelegatingResultSet.java:180)
at net.sf.hibernate.type.CalendarType.get(CalendarType.java:26)
at net.sf.hibernate.type.NullableType.nullSafeGet(NullableType.java:62)
at net.sf.hibernate.type.NullableType.nullSafeGet(NullableType.java:53)
at net.sf.hibernate.type.AbstractType.hydrate(AbstractType.java:66)
....

Un stack trace nos proporciona información muy valiosa para entender qué fue lo que ocurrió mal durante la ejecución de un programa en java. Mediante un análisis adecuado del stack trace podemos diagnosticar la causa del error, y esto puede ayudar a agilizar el proceso para resolverlo.

Resulta muy interesante observar el número de veces que un desarrollador pierde tiempo y esfuerzo en corregir un error por no saber leer correctamente el error reportado por un stack trace. Muchas veces el mensaje de error es muy claro y apunta directamente al problema, pero el programador comienza a corregir y cambiar cosas que nada tienen que ver con dicho problema. Esto naturalmente lleva a un desgaste y frustración innecesaria, que puede evitarse si se aprende a leer adecuadamente el mensaje de error.

Ejemplos como el siguiente son comunes en muchas organizaciones. El tiempo y recursos que se pierden por dar un mal seguimiento a un error muchas veces son considerables, y todo se inicia a partir de una labor inapropiada de análisis con respecto a un simple stack trace.



TIP: El proceso de solución de problemas es un proceso más sistemático que creativo. Te recomendamos acostumbrarte a leer cuidadosamente los mensajes de error y extraer toda la información que los mismos proporcionan antes de comenzar a dispersar tu imaginación con respecto a qué es lo que puede estar fallando en tu aplicación.

Un stack trace indica cuál es el flujo de ejecución de un programa o de un hilo de ejecución dentro de dicho programa hasta el momento en que se ocasionó un error. Esto puede sonar confuso, y para aclararlo pongamos el siguiente ejemplo. Supóngase que se invoca un programa java que inicia con el método main() definido en la clase com.example.PruebaAlta. Visualmente podríamos representar esta invocación como sigue:

Hay una forma de cuatro dígitos. Bajo este esquema el estándar de tres dígitos descrito arriba se convierte en los últimos tres dígitos del conjunto. El primer dígito representa permisos adicionales. En sistemas y programática donde no puede ser omitido este primer dígito del conjunto de cuatro, se establece cero como valor de éste.

Este es un escenario sencillo que involucra invocaciones sobre 3 clases distintas. Las invocaciones seguirían el siguiente flujo:

1.- Se invoca el método public static void main() de la clase com.example.PruebaAlta. La ejecución de un programa java tradicional típicamente inicia mediante la ejecución de este método.
2.- Como parte del método main() se invoca el método ejecutarProceso() dentro de la misma clase com.example.PruebaAlta.
3.- Como parte del método ejecutarProceso(), se invoca el método agregarDato() asociado a una instancia de la clase com.example.AdministradorAlta.
4.- Como parte del método agregarDato(), se invoca el método iniciar() asociado a una instancia de la clase sql.ConexionBD.
5.- Como parte del método iniciar(), se lanza la excepción java.lang.IllegalStateException. El mensaje asociado a la excepción en este ejemplo es “Bad Condition”.

Una vez lanzada esta excepción, ésta es propagada hasta el inicio del programa, y el programa finaliza, ya que en este caso no existe ningún bloque try/catch en ninguna parte que atrape y maneje dicha excepción. Una vez finalizado el programa, se imprimirá un stack trace similar al siguiente:

De modo tal puede considerarse la siguiente tabla:

java.lang.IllegalStateException: Bad Condition at sql.ConexionBD.iniciar(ConexionBD.java:49)
at com.example.AdministradorAlta.agregarDato(AdministradorAlta.java:32)
at com.example.PruebaAlta.ejecutarProceso(PruebaAlta.java:71)
at com.example.PruebaAlta.main(PruebaAlta.java:35)

Si examinamos cuidadosamente este listado, podemos ver que estas líneas describen cada uno de los pasos de ejecución que se habían mostrado en la lámina anterior, en orden inverso: Numerando cada uno de estos pasos obtenemos lo siguiente:

5.- java.lang.IllegalStateException: Bad Condition
4.- at sql.ConexionBD.iniciar(ConexionBD.java:49)
3.- at com.example.AdministradorAlta.agregarDato(AdministradorAlta.java:32)
2.- at com.example.PruebaAlta.ejecutarProceso(PruebaAlta.java:71)
1.- at com.example.PruebaAlta.main(PruebaAlta.java:35))

De esta manera podemos ver apreciar el valor de un stack trace para el proceso de diagnóstico y corrección de errores. Tan sólo mirando el stack trace anterior podemos hacer el siguiente análisis deductivo:

Análisis de la Línea 5:

5.- java.lang.IllegalStateException: Bad Condition

A partir de esta línea podemos decir que una excepción de tipo java.lang.IllegalStateException fue lanzada. El mensaje con el que esta excepción fue construida es “Bad Condition”.

Análisis de la Línea 4:

4.- at  sql.ConexionBD.iniciar(ConexionBD.java:49)

Esta es muchas veces la línea más importante del stack trace, además de la línea anterior que dice el tipo de excepción que fue lanzado. Esta línea nos dice que la excepción fue lanzada en la línea 49 del archivo ConexionBD.java. Esta línea corresponde al método iniciar() de la clase sql.ConexionBD.

En muchas ocasiones el nombre del archivo corresponderá al nombre de la clase, pero existen algunas excepciones
( clases internas, clases no públicas definidas en archivos con otro nombre ).

Si contamos con el código fuente con el que se compiló la clase, y vamos a la línea 49, encontraremos la línea de código donde la excepción fue lanzada. En una gran parte de las ocasiones, esto es suficiente para comprender el mensaje de error y porqué fue lanzado. En otras ocasiones se debe continuar con el análisis del stack trace en búsqueda de más pistas de porqué se generó el error.

Si el stack trace no contiene números de línea que indiquen el error, esto indica que la clase no fue compilada con información de depuración. Esta es generalmente una opción del compilador que puede habilitarse si se quiere generar este tipo de información para los .class. Por ejemplo, en Eclipse, estas propiedades se pueden controlar en las propiedades de un proyecto, en la sección del compilador:

Si estas cajas de selección se desmarcan y se reconstruye el proyecto, el stack trace aparecería como:

java.lang.IllegalStateException: Bad Condition
at sql.ConexionBD.iniciar(Unknown Source)
at com.example.AdministradorAlta.agregarDato(Unknown Source)
at com.example.PruebaAlta.ejecutarProceso(Unknown Source)
at com.example.PruebaAlta.main(Unknown Source)

En este caso sabríamos el método donde se generó el error, pero no el número de línea específica. En muchas ocasiones las librerías externas que utilicemos presentarán este detalle. Si se trata de librerías open source, la buena noticia ? es que podemos recompilarlas manualmente para evitar este problema en caso que se requiera.

Análisis de la Línea 3:

3.- at com.example.AdministradorAlta.agregarDato(AdministradorAlta.java:32)

Esta línea nos dice que el método iniciar() mencionado en la línea anterior fue invocado en la línea 32 del archivo AdministradorAlta.java, que corresponde al método agregarDato() de la clase com.example.AdministradorAlta. Todo esto es coherente con el escenario planteado inicialmente.

La mayoría de los IDEs modernos como Eclipse, al reportar un stack trace como parte de su ambiente de ejecución, ofrecen hiperligas hacia el lugar exacto en la ejecución del programa donde el error fue notificado. Esto nos facilita el ir directamente al lugar donde se generó cada uno de los errores asociados a un stack trace.

Análisis de la Línea 2:

2.- at com.example.PruebaAlta.ejecutarProceso(PruebaAlta.java:71)

Esta línea nos dice que el método agregarDato() mencionado en la línea anterior fue invocado en la línea 71 del archivo PruebaAlta.java, que corresponde al método ejecutarProceso() de la clase com.example.PruebaAlta.

Análisis de la Línea 1:

1.- at com.example.PruebaAlta.main(PruebaAlta.java:35)

Esta es la última línea del stack trace, lo que indica que el origen de ejecución del código inicia aquí. En este caso se dice que el método ejecutarProceso() mencionado en la línea anterior fue invocado en la línea 35 del archivo PruebaAlta.java, que corresponde al método main() de la clase com.example.PruebaAlta

Algunos stack traces pueden ser muy largos, lo cual es típico cuando se ejecutan programas java en application servers o ambientes de ejecución similares.

El que la última línea del strack trace muestre un método main() es típicamente indicativo de que se trata de la ejecución principal de un programa. Si la última línea del stack trace mostrara un método run(), esto indicaría que se trata de un hilo de ejecución adicional creado a partir de java.lang.Thread ( este será típicamente el caso para stack traces en un application server ):

1.- at com.example.PruebaAlta.main(PruebaAlta.java:35)

Constructores

En algunas ocasiones, se pueden mostrar stack traces como el siguiente:

java.lang.IllegalStateException: Bad Condition
at sql.ConexionBD.<init>(ConexionBD.java:12)
at com.example.AdministradorAlta.<init>(AdministradorAlta.java:15)
at com.example.PruebaAlta.ejecutarProceso(PruebaAlta.java:71)
at com.example.PruebaAlta.main(PruebaAlta.java:35)

Los métodos significan que la excepción fue lanzada en el constructor de la clase. En otras palabras sql.ConexionBD. significa un constructor de la clase sql.ConexionBD. El error mostrado en esta sección podría corresponder a un escenario como el de la siguiente imagen:

Conclusiones.

El análisis de un stack trace en java es sencillo, y permite obtener información importante con respecto al momento que se presentó un error durante la ejecución de una aplicación en java. De hecho, los stack traces son generalmente la fuente primaria para el diagnóstico de problemas en aplicaciones java de cualquier tamaño y tipo.

En la segunda parte de este documento se hablará acerca de excepciones anidadas. Otros documentos presentarán los tipos de excepciones más comunes en la plataforma java, y qué significan cada uno de ellos.


Linux Para Todos
http://www.linuxparatodos.net/portal/staticpages/index.php?page=interpretar_errores_1