Customize Grails Application Behavior Using the Event Model
A typical Grails application throws events at key points in the application life cycle, whether it's in the build process or at the creation of individual artifacts such as domain classes and controllers. The application events are good extension points to setup listeners to intercept them and react to the events with custom behavior. In addition to the traditional web application life cycle of HTTP requests and responses, Grails provides a number of custom touch points the developers can tap into the event model and provide custom application behavior. These touch-points include:
- the events thrown during the build process,
- startup and shutdown of the web application, and
- life cycle events of Grails domain classes.
The event handlers for Gant scripts, available since version 0.5, are triggered during execution of Grails target and plugin scripts. They are implemented (as everything else in Grails) as a collection of closures in the Events.groovy script, defined with a name beginning with "event". The event closures are called by Grails when the corresponding "events" get generated in the Gant script. Grails searches for these scripts in the following locations:
- USER_HOME/.grails/scripts - user-specific event handlers
- PROJECT_HOME/scripts - applicaton-specific event handlers
- PROJECT_HOME/plugins/*/scripts - plugin-specific event handlers
The event handling in Grails can be managed both at macro and micro levels. The macro level is build and application wide and the micro level happens at the domain layer, where domain classes publish the events whenever they interact with the database. Scott Davis recently wrote about the Grails event model and how the events throughout Grails application life cycle can help the developers further customize the application behavior. The developers can extend the build process without modifying the standard Grails scripts by creating Events.groovy file and placing the custom code in it.
All steps in the build process between "grails create-app" and "grails run-app" (or "grails war") commands have events that are thrown at key points. You can tap into these build events in your project by creating a listener. These events can also be used for post-installation configuration and application upgrades. When customizing the scripts in GRAILS_HOME/scripts (to throw custom events), Scott recommends these files be copied into scripts directory in the project so that the custom scripts are also checked into source control along with everything else.
In addition to build events, Grails developers can also catch application events for customization of the application logic. The BootStrap file located in grails-app/conf folder runs every time Grails starts and stops. The init closure in the file gets invoked on startup and the destroy closure gets called when the application is shut down. To view the debug messages when both the init and destroy events fire, start up Grails in interactive mode by typing:
An example of extending the application events is to add new records into the database at the bootstrap time. Scott talked about Failsafe database inserts and deletes in BootStrap.groovy to avoid duplicate records in the database. He also explained the environment-specific (Development, Test, or Production) actions in Bootstrap script. using the GrailsUtil class. Import grails.util.GrailsUtil at the top of the script and call the static method GrailsUtil.getEnvironment() (method call can be shortened to simply GrailsUtil.environment because of Groovy's shorthand getter syntax) to figure out what mode you are running in.
Domain Class Events:
These include four event hooks for CRUD operations: onLoad, beforeInsert, befortUpdate, and beforeDelete which are triggered when database operations are called.
- onLoad gets called when the class is loaded from the database.
- beforeInsert closure gets called before the save() method.
- beforeUpdate closure gets called before the update() method.
- beforeDelete closure gets called before the delete() method.
To supplement the above events, Grails supports GORM Events and Hibernate plugin. GORM events customization include the registration of events that get fired when certain events such as deletes, inserts and updates occur. Hibernate events plugin adds support to domain models for hooking into the hibernate event system using the methods: afterInsert, afterUpdate, afterDelete, beforeLoad, afterLoad, beforeSave, and afterSave. Other domain layer event is the timestamping of domain classes. If you provide a couple of specially named fields (lastUpdated and dateCreated), GORM automatically timestamps the class.
Grails also supports intercepting Auto Reload Events that can be used to monitor resources for any data changes and then reload the changes when they occur. This is how Grails implements the reloading of application state at runtime.