Friday, February 04, 2011

Handling Errors in Spring Framework

To handle errors in your Spring Framework application you will need to play with two files: web.xml and applicationContext.xml (or whatever the name is for your application context configuration file).

web.xml

<error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/WEB-INF/jsp/uncaughtException.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/WEB-INF/jsp/uncaughtException.jsp</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/jsp/notFound.jsp</location>
    </error-page>

Note how I use the same JSP for 500 errors and Exceptions. From that JSP I log the trace in the server logs (so I can monitor problems occurring in user browsers for example) Below is spring pet clinic uncaughtException.jsp modified to log the complete stacktrace:
<%@ page isErrorPage="true" %>
<%@ page  import="org.slf4j.Logger" %>
<%@ page  import="org.slf4j.LoggerFactory" %>
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<%@ include file="/WEB-INF/jsp/header.jsp" %>

<%
final Logger log = LoggerFactory.getLogger("com.nestorurquiza.web.internal-error");
log.error("Internal Server Error", exception);
%>

Internal error

<% try { // The Servlet spec guarantees this attribute will be available //Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception"); if (exception != null) { if (exception instanceof ServletException) { // It's a ServletException: we should extract the root cause ServletException sex = (ServletException) exception; Throwable rootCause = sex.getRootCause(); if (rootCause == null) rootCause = sex; out.println("** Root cause is: "+ rootCause.getMessage()); rootCause.printStackTrace(new java.io.PrintWriter(out)); } else { // It's not a ServletException, so we'll just show it exception.printStackTrace(new java.io.PrintWriter(out)); } } else { out.println("No error information available"); } // Display cookies out.println("\nCookies:\n"); Cookie[] cookies = request.getCookies(); if (cookies != null) { for (int i = 0; i < cookies.length; i++) { out.println(cookies[i].getName() + "=[" + cookies[i].getValue() + "]"); } } } catch (Exception ex) { ex.printStackTrace(new java.io.PrintWriter(out)); } %>


<%@ include file="/WEB-INF/jsp/footer.jsp" %>



applicationContext.xml

<bean class="com.nestorurquiza.web.handler.CustomSimpleMappingExceptionResolver" >
        <property name="exceptionMappings">
            <props>
                <prop key="org.springframework.web.servlet.PageNotFound">notFound</prop>
                <prop key="java.lang.Exception">failure</prop>
            </props>
        </property>
    </bean>

Note I use a custom MappingExceptionResolver. I do this again to make sure I log the error. I strongly believe error monitoring must be done from outside the application.

Below is the code for CustomSimpleMappingExceptionResolver:
package com.nestorurquiza.web.handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

public class CustomSimpleMappingExceptionResolver extends SimpleMappingExceptionResolver{
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        final Logger log = LoggerFactory.getLogger(CustomSimpleMappingExceptionResolver.class);
        log.error(null,ex);
        return super.resolveException(request, response, handler, ex);
    }
}

No comments:

Followers