Spring Framework Error Controller with Freemarker

3.7k views Asked by At

I have problem. Every time I have exception happening my spring error controller either bypasses my sitemash-freemarker decorator and just shows the error dump. Or it includes the decorator but doesn't put in the user session so the personalization in the decorator is gone.

How do you properly integrate exception handling in spring using freemarker?

Extract from web.xml:

//standard sitemash decorator and freemarker setup
<error-page>
    <error-code>404</error-code>
    <location>/pageNotFound.html?code=404</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/servletErrorView.html?code=500</location>
</error-page>
<error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/error.html</location>
</error-page>

ErrorController.java:

@Controller
public class ErrorController extends AnnotationMethodHandlerAdapter {

private static final Logger log = Logger.getLogger(ErrorController.class);

@Autowired
private ComponentHelper helper;
@Autowired
private MailNotificationService mailNotificationService;

@RequestMapping(value = {"/servletErrorView.html", "/error.html"}, method = RequestMethod.GET)
protected ModelMap showError(HttpServletRequest req) {

    String code = null, message = null, type = null, uri = null;
    Object codeObj, messageObj, typeObj;
    Throwable throwable;

    ModelMap mm = helper.getMM();
    //todo handle org.springframework.web.bind.MissingServletRequestParameterException

    codeObj = req.getAttribute("javax.servlet.error.status_code");
    messageObj = req.getAttribute("javax.servlet.error.message");
    typeObj = req.getAttribute("javax.servlet.error.exception_type");
    throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
    uri = (String) req.getAttribute("javax.servlet.error.request_uri");

    // Convert the attributes to string values
    if (codeObj != null) code = codeObj.toString();
    if (messageObj != null) message = messageObj.toString();
    if (typeObj != null) type = typeObj.toString();

    // The error reason is either the status code or exception type
    String reason = (code != null ? code : type);
    if (uri == null) {
        uri = req.getRequestURI();
    }
    log.error("ErrorController\n reason:"+reason+"\n message:"+message+"\n uri:"+uri+"\n ",throwable);

    mm.addAttribute("message", "<H4>" + reason + "</H4>" +
            "<H4>" + message + "</H4>" +
            "<P>" + ((throwable != null) ? getStackTrace(throwable) : "") + "</P>" +
            "<I>Error accessing " + uri + "</I>");

    String subject = "Error - "+reason;
    String freemarkerTemplet = "/WEB-INF/freemarker/errorMail.ftl";

    mailNotificationService.sendEmail(subject, "[email protected]", mm, freemarkerTemplet);

    return mm;
}

public static String getStackTrace(Throwable aThrowable) {
    //add the class name and any message passed to constructor
    final StringBuilder result = new StringBuilder("Trace: ");
    result.append(aThrowable.toString());
    final String NEW_LINE = "<br>";
    result.append(NEW_LINE);

    //add each element of the stack trace
    for (StackTraceElement element : aThrowable.getStackTrace()) {
        result.append(element);
        result.append(NEW_LINE);
    }
    return result.toString();
}
}

error.ftl

<html>
<#import "/spring.ftl" as spring/>
<#import "common.ftl" as common/>
<head>

<title>ITeezy: Error page</title>
    <meta name="description" content="Error"/>
</head>

 <body id="error">

 <div id="main">
  <h1>Error</h1>
<p><a href="<@spring.url "/index.html"/>"> <- Go back to Homepage</a></p><br>
 <div id="logo"> <a href="<@spring.url "/index.html"/>"> <img src="/images/mainlogo.png" alt="[Logo]" width="260" height="140"/> </a> </div>

 <#if message?exists>
     <div class="message">Error: ${message}</div>
 </#if>
  <br>
We're sorry you received an error. Please do us a favor and let us know!
Email: <img src="/images/support-email.png" width="118" height="13"/>
with the error message and a description of what you were doing. Thanks!

<#if exception?exists>
    ${exception}
    <#list exception.stackTrace as st>
        ${st}
    </#list>
<#else>
    <#if javax?exists && javax.servlet?exists && javax.servlet.error?exists && javax.servlet.error.exception?exists>
        Servlet Exception:<p>
        ${javax.servlet.error.exception} <br>
        ${javax.servlet.error.exception.message?default("")} <br>
        <#list javax.servlet.error.exception.stackTrace as st>
            ${st}<br>
        </#list>
    </#if>
</#if>

</div>
</body>
</html>
1

There are 1 answers

2
Jiří Vypědřík On BEST ANSWER

Something like this:

<bean id="errorMapping" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="defaultErrorView" value="error/default"/>
    <property name="defaultStatusCode" value="500"/>
    <property name="exceptionMappings">
        <props>
            <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">error/uploadsize</prop>
        </props>
    </property>
</bean>

...and you'll get the object named exception in your model. No web.xml configuration is required. You can specify either custom views for particular exception classes or fall back to the default view. You can as well override one of the standard resolvers to populate the model with additional data, for instance, to dump the stacktrace into a string and display it on the exception page under a spoiler block.