Monitoring Resources for Changes
Often it is valuable to monitor resources for changes and then reload those changes when they occur. This is how Grails implements advanced reloading of application state at runtime. For example, consider the below simplified snippet from the
ServicesPlugin
that Grails comes with:
class ServicesGrailsPlugin {
…
def watchedResources = "file:./grails-app/services/*Service.groovy" …
def onChange = { event ->
if(event.source) {
def serviceClass = application.addServiceClass(event.source)
def serviceName = "${serviceClass.propertyName}"
def beans = beans {
"$serviceName"(serviceClass.getClazz()) { bean ->
bean.autowire = true
}
}
if(event.ctx) {
event.ctx.registerBeanDefinition(serviceName,
beans.getBeanDefinition(serviceName))
}
}
}
}
Firstly it defines a set of
watchedResources
as either a String or a List of strings that contain either the references or patterns of the resources to watch. If the watched resources is a Groovy file, when it is changed it will automatically be reloaded and passed into the
onChange
closure inside the
event
object.
The
event
object defines a number of useful properties:
event.source
- The source of the event which is either the reloaded class or a Spring Resource
event.ctx
- The Spring ApplicationContext
instance
event.plugin
- The plugin object that manages the resource (Usually this)
event.application
- The GrailsApplication
instance
From these objects you can evaluate the conventions and then apply the appropriate changes to the
ApplicationContext
and so forth based on the conventions, etc. In the "Services" example above, a new services bean is re-registered with the
ApplicationContext
when one of the service classes changes.
Influencing Other Plugins
As well as being able to react to changes that occur when a plugin changes, sometimes one plugin needs to "influence" another plugin.
Take for example the Services & Controllers plugins. When a service is reloaded, unless you reload the controllers too, problems will occur when you try to auto-wire the reloaded service into an older controller Class.
To get round this, you can specify which plugins another plugin "influences". What this means is that when one plugin detects a change, it will reload itself and then reload all influenced plugins. See this snippet from the
ServicesGrailsPlugin
:
def influences = ['controllers']
Observing other plugins
If there is a particular plugin that you would like to observe for changes but not necessary watch the resources that it monitors you can use the "observe" property:
def observe = ["controllers"]
In this case when a controller is changed you will also receive the event chained from the controllers plugin. It is also possible for a plugin to observe all loaded plugins by using a wildcard:
The Logging plugin does exactly this so that it can add the
log
property back to
any artefact that changes while the application is running.