18/09/2013
En el presente tip vamos a ver algunos conceptos básicos de Struts 2 que nos ayudarán a la hora de gestionar las excepciones que no controlemos y que «escapen» de nuestra aplicación web. El proyecto de ejemplo tendrá una única acción que forzará el lanzamiento de una excepción;su código no puede ser más simple:
package com.danielme.tips.struts2.actions; /** * * @author danielme.com * */ public class MainAction { public String execute() { int num = 5/0; return null; } }
Si tenemos activado devMode en el struts.xml (desaconsejado en producción para no penalizar el rendimiento), veremos la traza del error en una pantalla propia de Struts 2:
En caso contrario, veremos una página de error de Tomcat al menos en mi caso que uso Tomcat 7:
Para gestionar estas excepciones incontroladas, vamos a llevar a cabo dos acciones
- Asegurar que estas excepciones quedan registradas en el sistema de log, en mi caso log4j. Esto se consigue configurando el interceptor exception, el primero de la pila de interceptores por defecto de Struts 2 y que también debería serlo de la nuestra para que sea el último en ejecutarse durante el postproceso y pueda «atrapar» incluso las excepciones producidas dentro de interceptores. Asimismo, guarda la excepción en el ValueStack lo que nos permitirá recuperarla tal y como más adelante en la jsp de error.
<?xml version="1.0" encoding="UTF-8" ?> <interceptors> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"> <param name="exception.logEnabled">true</param> <param name="exception.logLevel">ERROR</param> </interceptor-ref> </interceptor-stack> </interceptors>
- Mostrar una página de error personalizada y que sea consecuente con los estilos de nuestra aplicación, definiendo un result para cada tipo de excepción. El global result puede ser un Action en el que podremos llevar a cabo alguna tarea adicional al registro del error en el log, realizado de forma automática, y sin necesitar de implementar nuestra propia versión del interceptor.
<global-results> <result name="exception">/jsp/error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping result="exception" exception="java.lang.Exception" /> </global-exception-mappings>
Con estos cambios, el struts.xml quedará tal que así:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="false" /> <constant name="struts.custom.i18n.resources" value="global" /> <package name="default" namespace="/" extends="struts-default"> <interceptors> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"> <param name="exception.logEnabled">true</param> <param name="exception.logLevel">ERROR</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="myStack" /> <global-results> <result name="exception">/jsp/error.jsp</result> </global-results> <global-exception-mappings> <exception-mapping result="exception" exception="java.lang.Exception" /> </global-exception-mappings> <action name="mainAction" class="com.danielme.tips.struts2.actions.MainAction" /> </package> </struts>
La página /jsp/error.jsp es la siguiente, obsérvese cómo podemos mostrar los detalles de la excepción. En ese caso, lo habitual es ocultar esta información en un div desplegable pero mi ejemplo no es tan detallista.
<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <%@include file="/jsp/head.jsp" %> </head> <body> <h3><s:text name="message"/></h3> <p><b>exception.message:</b> <s:property value="exception.message"/></p> <p><b>exceptionStack:</b> <s:property value="exceptionStack" /></p> <%@include file="/jsp/footer.jsp" %> </body> </html>
El proyecto completo para Maven se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.