Experimental Grails plugin implementing declarative exception handling.
I never got any feedback at the mailinglist when I asked for best practices regarding exception handling in controllers. The aim of this plugin is to reduce noise in controllers and make it easier to implement consistent error handling.
A large part of my love for Groovy and Grails comes from how successfully they reduce Java boilerplate code while at the same time increase readability. This plugin is an attempt to remove some redundant code from controllers and make it easier to implement consistent error handling.
Grails already supports mapping of exceptions to views or controller actions, but it doesn't really do what I want. It only works for http 500 responses and all exceptions will be logged. There is no way (at least as far as I know) to actually deal with exceptions that you're able to recover from.
It's obviously possible to deal with this using try / catch, but it's noisy and you'll probably implement exactly the same error handling for the same exceptions over and over again so it's not very dry.
// Noisy, not dry
class FridgeController {
def milkFactory // implements the interface Cow
// This is a lot of noise
def getMilk = {
def milk = null
try {
milk = milkFactory.getMilk()
} catch (NoMoreMilkException mex) {
redirect controller: 'shoppingList', action: 'addItem', params: [ item: 'milk' ]
return
}
[ milk: milk ]
}
}
The plugin will look for a static exceptionMappings
closure in any of your *UrlMappings
files. It will also pick up any changes in these files during development.
class UrlMappings {
static mappings = {
// Regular URL mappings still goes here..
}
static exceptionMappings = {
"no more milk" NoMoreMilkException, { ex ->
controller = "shoppingList"
action = "addItem"
item = "milk"
}
}
}
// A lot less noisy, does the same thing
class FridgeController {
def milkFactory
def getMilk = {
[ milk: milkFactory.getMilk() ]
}
}
Each mapping is defined by a name, an exception class and a closure. The name is only there for debugging and readability reasons.
The rules will be checked in the order they are defined so make sure to define the most specific exceptions in your exception hierarchy first.
The closure is executed when a rule matches an exception instance. You're expected to set the controller and action property. Any other properties you set in the closure will be made available for the controller action as properties in params
.
- Controllers gets a lot less noisy.
- You only specify once how you want to deal with certain exception.
The last point is the biggest win as far as I'm concerned. Let's say your users has to accept an agreement to use certain features on your site. Instead of doing if (user.hasAgreed())
everywhere you can just throw an MissingUserAgreementException
and trust that the user will be redirected to the agreement page.
I don't know how likely this is to change in new versions of Grails, but as always when messing around with platform beans there is a chance that things might change in newer versions of Grails.
Although if Grails 2.0 actually makes the switch from closure actions to methods things like these will be a lot easier to implement using aspects.
None, that I'm aware of. Do tell me if I've overlooked some body traps!
Grails will construct the exceptionHandler
bean as usual. A bean post processor replaces it with this plugin's implementation of HandlerExceptionResolver
and passes it a reference to the original handler. Whenever an exception occur that doesn't match any of the exception mapping rules it will be passed to the original handler.
The plugin is piggybacking on the UrlMappings plugin. Changes to *UrlMappings.groovy
will be picked up by this plugin as well.
- It sometimes stops working after a container reload, I haven't looked into this yet