Thursday, 16 February 2012

Using Spring’s SimpleMappingExceptionHandler

My last few blogs have talked about Spring’s @ExceptionHandler annotation and how you can use it to deal with errors on a controller by controller basis to give you fine-grained control over how you handle errors in your code. The question to consider now is whether or not you always want such fine grained control, to which I’m guessing that in certain circumstances the answer will be no, and so to accommodate this Spring have provided us with the SimpleMappingExceptionHandler.

The SimpleMappingExceptionHandler is an implementation of Springs HandlerExceptionResolver class, which, as I’ve mentioned before, Spring uses to manage exceptions thrown by your code. Spring has a limitation (if that’s what it is) in that it only loads one HandlerExceptionResolver implementation at any one time. The default implementation is AnnotationMethodHandlerExceptionResolver as mentioned in my previous blogs, which forces you to make a choice between using fine-grained exception handling as provided by AnnotationMethodHandlerExceptionResolver and more coarse grain exception handling provided by SimpleMappingExceptionHandler.

The Guy’s at Spring have made adding a SimpleMappingExceptionHandler to your app fairly straight forward. To demonstrate this I first of all need a flakey controller that will throw an exception for us. The code below, lifted from my previous blog, throws an IOException when called...

  @RequestMapping(value = "/ioexception", method = RequestMethod.GET)
 
public String throwAnIOException(Locale locale, Model model)
     
throws IOException {

   
logger.info("This will throw an IOException");

   
boolean throwException = true;

   
if (throwException) {
     
throw new IOException("This is my IOException");
   
}

   
return "home";
 
}

...and is guaranteed to break a webapp1

If you were intending to keep things ultra-simple, the final step would be to add the following XML to your Spring config file:

 <beans:bean id="exceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
  <beans:property name="exceptionMappings">
   <beans:map>
    <beans:entry key="java.io.IOException" value="io-exception" />
    <beans:entry key="java.lang.Exception" value="generic-error" />
   </beans:map>
  </beans:property>
 </beans:bean>

The XML above shows a very familiar Spring bean definition. The bean name is “exceptionResolver”, implemented by the SimpleMappingExceptionResolver class. This class has a map property, which is the most interesting part of this definition as it maps an exception class to a view name. In the sample I’m mapping IOException to a view called ‘io-exception’ and all other exceptions to a view called ‘generic-error’. Note that SimpleMappingExceptionHandler follows the standard Java exception handling rules, so when I say ‘all other exceptions’ I really mean all exceptions except IOException and its subclasses.

Although that about wraps it up for a trivial implementation. I’d like to add that I prefer to extend SimpleMappingExceptionHandler so that I can add in additional functionality, such as application specific error logging:

public class SampleExceptionHandler extends SimpleMappingExceptionResolver {

 
private static final Logger logger = LoggerFactory.getLogger(SampleExceptionHandler.class);

 
/**
   * Log the exception.
   *
   *
@see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver#doResolveException(javax.servlet.http.HttpServletRequest,
   *      javax.servlet.http.HttpServletResponse, java.lang.Object,
   *      java.lang.Exception)
   */
 
@Override
 
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
      Exception ex
) {
   
logger.error("A " + ex.getClass().getSimpleName() + " has occured in the application", ex);

   
return super.doResolveException(request, response, handler, ex);
 
}

}

Once you’ve written your own exception handler, then all you have to do is to modify your Spring config so that it’s picked up by Spring when the webapp loads:

<!-- Definition(s) for the SimpleMappingExceptionResolver -->
 <beans:bean id="exceptionResolver"
  class="com.captaindebug.exceptions.SampleExceptionHandler">
  <beans:property name="exceptionMappings">
   <beans:map>
    <beans:entry key="java.io.IOException" value="io-exception" />
    <beans:entry key="java.lang.Exception" value="generic-error" />
   </beans:map>
  </beans:property>
 </beans:bean>



1 The full code for this blog is available from:

git://github.com/roghughe/captaindebug.git

6 comments:

tkt said...

If I'm correct, this would show an Error view, but the response status would still be 200. How would you change the status to 500, etc in this setup?

tkt said...

If I'm correct, this would return the error view but the response status would still be 200. How do you change the status to 500, 404, etc in this situation?

Roger Hughes said...

I have a whole series of blogs on Spring Exception handling. I think that you'll find what you're looking for in my blog on the @ResponseStatus annotation, which allows you to change the response code to whatever value you like.

Roger Hughes said...

tkt,
Also note that using the SimpleMappingExceptionHandler might not be the best way of achieving your aims as this is Spring's older method of dealing with exceptions. If you read through the blogs I've written you'll see that Spring has other approaches to this problem, which may be more suitable.

Anonymous said...

Thanks for the response! I've read through all your posts...good stuff!

So, the obvious answer would be to use the ExceptionHandler and ResponseStatus annotations. I'm thinking that it might be worth creating a parent controller to house at least the more generic handleExceptions methods. ...might try that out.

I could also extend SimpleMappingExceptionResolver and map up statuses to exceptions there. I think the only 2 I plan on returning would be 404 and 500. Either way, returning an error page with status 200 is bad for SEO.

Another issue I've found with SimpleMappingExceptionResolver is that it's a view resolver and not a controller. I'm a bit confused by this actually, but I think the significance is that if you have a 404 page that needs data for it's header and footer and usually would get that from a controller it won't get that data because it's controller isn't run at this stage. If I'm correct, then that's a fairly large issue because you can only serve static content unless you do some work to get the data in your SimpleMappingExceptionResolver subclass. That doesn't seem like a responsibility of a view resolver! I've found through trial and error though that the web.xml error-page configs seem to run the full request over again.

...long one, but I welcome any of your thoughts on this!

tkt

Roger Hughes said...

Hi Anonymous,
You're right in saying that the SimpleMappingExceptionResolver is just a view resolver, but the question t ask here is, so far as your code goes, whats the difference between a view resolver and a controller as both aim to return a model and view back to the app for display.

In overriding

protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex)

you have full control of what's going on. Firstly you can return a ModelAndView object containing the name of the view and any data you want to add to it. Secondly, I think that the 'Object handler' argument is a reference to your controller (it certainly is in the complementary Spring HandlerInterceptorAdapter class), so you should theoretically be able to access any of it's properties and create a detailed error page.

If I'm wrong here please let me know...