Capturar excepciones en el AWT Thread de una aplicación Swing

20 02 2010

En java podemos clasificar las excepciones en dos tipos, las “checked” y las que no son “not-checked” (perdonad, pero no sé cómo traducirlo satisfactoriamente).

Los métodos que lanzan excepciones del primer tipo ( cheched ) fuerzan al cliente de dicho métodos a tratarlas con un bloque try/catch o a lanzarlas para que sea un antecesor en la pilla de llamadas quien las trate. En última instancia la excepción alcanza el método principal main, que si no la gestiona la ‘lanza’ al sistema operativo y la aplicación muere con el volcado de pila del lugar donde se produjo.

Las excepciones not-checked no tienen esta restricción: ni es obligatorio capturarlas, ni el método cliente tiene que declarar explícitamente su lanzamiento mediante una cláusula throws en su signatura.

Las excepciones not-checked son todas aquellas que heredan de la clase RuntimeException . Así pues, si un método puede lanzar NullPointerException o IllegalArgumentException (entre otras), no está obligado a declararlo en su signatura ni el cliente a gestionar la excepción. De todos modos, si se produce, su propagación sigue la misma política: van escalando la pila de llamadas hasta que alguien las trate.

A la hora de programar una aplicación somos muy conscientes de las excepciones checked porque su no gestión es un error en tiempo de compilación, sin embargo para las excepciones not checked no tenemos más remedio que prever su posibilidad y hacer la gestión correspondiente donde corresponda. No obstante es posible que mientras estamos desarrollando o depurando no hayamos previsto una situación que desencadene el lanzamiento de una excepción de este tipo (típicamente se nos puede escapar alguna NullPointerException ). En estos casos (y siempre en general) es útil tener un bloque catch que capture el ‘resto’ de excepciones que el programador no previó e intentar hacer algo útil y más elegante que acabar la aplicación abruptamente (como por ejemplo guardar información en los logs que nos permita depurar o hacer un diagnóstico del problema).

¿Cómo y dónde ponemos este bloque “ captura el resto de excepciones”? En aplicaciones con un solo hilo de ejecución es muy fácil: en el main como ilustra el siguiente ejemplo.

public static void main(String[] args) {
try {
	// mi super-programa
} catch (Throwable e) {
	logger.debug("Unexpected exception!", e);
}

¿Pero qué pasa en una aplicación multi-hilo? En este caso el main creará varios hilos que tendrán su propia pila de llamadas por lo que cualquier excepción producida y no capturada en cualquiera de esos hilos no podrá ser capturada por el catch del ejemplo anterior.

En particular, las aplicaciones Swing son siempre multi-hilo. El código de programación de todos los listeners de gestión de eventos producidos por los diferentes elementos de la GUI, se ejecuta dentro del event dispatch thread (el hilo de gestión de eventos) también conocido como “AWT” Thread y que no es el mismo en el que se ejecuta el main del programa.

¿Qué pasa si se produce una excepción not-checked en este hilo? Pues que si no lo hemos previsto en el código y no hay ningún catch que la capture, se pierde irremediablemente. Afortunadamente existe una solución que expondré a continuación y que también está explicada en este post.

La API estándar de Java ofrece la clase ThreadGroup . Cuando creamos un thread podemos hacer que pertenezca a una instancia de esta clase. Dicha clase tiene un método con la siguiente signatura:

void uncaughtException(Thread t, Throwable e)

que es llamado por la máquina virtual de forma automática cuando cualquiera de los threads que forman parte de ese grupo lanza una excepción que nadie captura. Cuando un hilo se crea dentro de un grupo, todos los hilos que pueda crear ese hilo también pertenecen al mismo grupo.

Ahora ya tenemos todos los elementos que solucionan nuestro problema:

  1. Creamos una subclase de ThreadGroup que sobrescriba el método uncaughtException para que haga algo útil en nuestro programa.
  2. En el main construimos una instancia de la clase que acabamos de construir.
  3. Creamos un thread que se encarga de montar toda la GUI que forma parte del grupo recién instanciado.

Con esto conseguimos que todos los threads interesantes de nuestro programa pertenezcan al grupo que es capaz de gestionar las excepciones no capturadas.

Nuestro ThreadGroup podría tener el siguiente aspecto:

class HandleExceptionGroup extends ThreadGroup {
	private static Logger logger = Logger.getLogger(ExceptionGroup.class);

	public HandleExceptionGroup() {
		super("ExceptionGroup");
	}

	@Override
	public void uncaughtException(Thread t, Throwable e) {
		String msg =
			"GUI produced an unexpected exception (thread: " + t.getName() + ") ";

		logger.warn(msg, e);
	}
}

y el main de la clase principal:

public static void main(String[] args) {
        HandleExceptionGroup threadGroup = new HandleExceptionGroup();
	new Thread(threadGroup, "Main Thread"){
		@Override
		public void run() {
			// aquí construyo la GUI
		}
	}.start();
}
Anuncios

Acciones

Information

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s




A %d blogueros les gusta esto: