Tuesday, 7 February 2012

Spring 3 MVC Exception Handlers

The majority of the Spring 3 error handling sample code that I’ve come across only ever seems to give the simplest overview of its usage, yet how we handle errors is, some would say, more important than how the normal code works. This was borne out the other day when I came across a simple ‘GOTCHA’ in a Spring 2 error handler that brought a whole website down and almost killed the server, but more on that later.

Today’s blog examines the scenario of creating a simple Spring 3 servlet exception handler using the @ExceptionHandler annotation. Although you may have seen this before it provides me with a good place to start, and for this demonstration I’ve created a simple Spring 3 MVC webapp1 where the home page (home.jsp) makes requests to a flaky controller class that throws exceptions (ExceptionDemoController)

  /**
   * Whoops, throw an IOException
   */
 
@RequestMapping(value = "/ioexception", method = RequestMethod.GET)
 
public String throwIoException(Locale locale, Model model) throws IOException {

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

   
boolean throwException = true;

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

   
return "home";
 
}

The flaky controller code above is the first step in generating an error. The idea is that it’s supposed to return the user to our home page, but in the mists of processing the user’s request it throws a simple IOException. Once thrown, the exception is caught by this method:

  /**
   * Catch IOException and redirect to a 'personal' page
   */
 
@ExceptionHandler(IOException.class)
 
public ModelAndView handleIOException(IOException ex) {

   
logger.info("handleIOException - Catching: " + ex.getClass().getSimpleName());
   
return errorModelAndView(ex);
 
}

 
/**
   * Get the users details for the 'personal' page
   */
 
private ModelAndView errorModelAndView(Exception ex) {
   
ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName
("error");
    modelAndView.addObject
("name", ex.getClass().getSimpleName());
    modelAndView.addObject
("user", userDao.readUserName());

   
return modelAndView;
 
}

To set this up is really simple, all you need to do is to add:

  @ExceptionHandler(IOException.class)

… to a method signature, et voila you’re done ...and that’s the simple bit over with.

There are some points worth noting here: firstly, using

  @ExceptionHandler(IOException.class)

…will adhere to the usual contract for exception handling. This means that not only will the method above catch all IOExceptions, it’ll also catch all exceptions that are subclasses of IOException; hence, if my throwAnException(..) method threw a FileNotFoundException it’ll still be caught by my handleIOException(...) method.

Secondly, there is a very flexible, but ultimately limited, set of method signatures that you can use for exception handler methods. The full documentation for this is provided by Spring’s JavaDoc, but in summary you can devise a signature that contains any of the following input arguments in any order:
  • Exception or one of its subclasses
  • ServletRequest or HttpServletRequest
  • ServletResponse or HttpServletResponse
  • HttpSession
  • WebRequest or NativeWebRequest
  • Locale
  • InputStream or one of its subclasses to access the request’s content
  • OutputStream or one of its subclasses to access the response’s content
  • Reader or one of its subclasses
  • Writer or one of its subclasses

The method signature must also have one of the following return types:
  • ModelAndView
  • Model
  • Map
  • View
  • String - interpreted as a view name
  • void, but only if the method writes directly to the response object

All of which should be enough for any scenario under any circumstance.

Using @ExceptionHandler gives you the ability to perform fine grained exception handling that targets different error scenarios. In the case of the sample code, I create a new ModelAndView object and populate it with the user’s name in order to personally tell him/her that the system has lost their documents. Some may say that this is a limitation, as @ExceptionHandler is so fine-grained that you can only catch exceptions thrown by the controller that contains your @ExceptionHandler annotated method. I would disagree, if you want to catch exceptions thrown by multiple controllers in one place, then this technique is not for you and you should consider using a SimpleMappingExceptionResolver instead.

There’s lots to consider when implementing error handling such as: what happens if there’s an error in your error handler? Should you use coarse or fine grained exception handlers? What about setting the HTTP status code? So, my next few blogs will looking into error handling further, demonstrating how to assign multiple exception classes to a single @ExceptionHandler and how to combine the exception handler notation with @ResponseStatus to fine tune your server’s HTTP status code, and may be more...


  1. The full webapp sample is available at:

    git://github.com/roghughe/captaindebug.git
  2. See the Spring documentation for reference material.

4 comments:

OEF-Vet AliasedHacker said...

In your exception handling you are attaching the username as fetched from userdao to the page. What if userdao is the page in error? Your error page will not work.

Thank you for the blog. It is interesting to see people code along the same lines of what you come up with on your on.

Roger Hughes said...

OEF-Vet AliasedHacker
Thanks for the comment and a good question. One of the points I was trying to make about the Spring MCV Exception handler paradigm is that it allows you to apply both fine and coarse grained tuning to your error handling, which means that you can target specific exceptions to specific code. Therefore if you know that, for example, an IOException will NEVER be associated with your database, then there's no reason why you can't access the database to provide really user friendly error pages.

As I said in my blog, on of the problems to consider when writing an exception handler is to consider what happens if it throws an exception.

I also have three other blogs in this series, which you may find useful:

Spring 3 MVC Exception Handlers and Multiple Exception Arrays

and

Spring 3 MVC Exception Handlers and @ResponseStatus

and

Using Spring’s SimpleMappingExceptionHandler

Don Stevo said...

I have a question.

I'm not sure if @ExceptionHandler serves this purpose.

All explanation can be found in my forum topic post:
http://forum.springsource.org/showthread.php?127846-ExceptionHandler-form-data-question&p=417250#post417250

I have a detail page where I have a remove button. If I click that remove button, and a FK constraint violation throws a JpaSystemException, I want a clean message to be displayed to the user ON the detail page instead of on a separate error.jsp

In one way, using a submit button that also submits the object's ID, I can fetch the ID of the object from the request, and re-populate the model.

But in another way, the object's ID is set on the URL using a @PathVariable. You cannot use @PathVariable in @ExceptionHandler methods.

Do you use @ExceptionHandler methods for this? Or don't they supose to work this way?

A solution to my problem is just catching the Exception in the controller's @RequestMapping annotated method itself, and handling it over there.

But what then is the purpose of @ExceptionHandler?

Thanks for your answer.

Roger Hughes said...

Don
This is an interesting problem, and given the information provided, I think that in this case I’d tackle the problem by using your first possible solution of not using the @ExceptionHandler.

The motivation for this approach is that I’d be differentiating between business exceptions and application exceptions. Just to clarify this: a business exception is just an alternate flow to the scenario you’re coding. In your case nothing appears to have gone wrong; a JpaSystemException has been thrown because the user isn’t allowed to remove the region as it’s still referenced by a country through an FK. This appears to be a normal alternate flow.

An application exception, on the other hand, is when something goes wrong with the app: the database is down, there’s a coding issue that’s not been spotted in testing, it’s run of of memory etc. In this case I’d definitely opt for an @ExceptionHandler that would display a separate error page.

It’s important, when designing an app, to differentiate between how you handle business exceptions and how you handle application exceptions and apply appropriate, but different paradigms.

I think that in the case of a business exception it’s perfectly reasonable to use your try/catch approach to populate the model with a message that the JSP can pick up when it redisplays your detail page.

Another reason for using the try/catch approach in this particular scenario is that the code appears simpler and easier to read, making it more maintainable.