Grails provides a number of extension points that allow you to extend anything from the command line interface to the runtime configuration engine. The following sections detail how to go about it.
Creating Plugins
Creating a Grails plugin is a simple matter of running the command:
grails create-plugin [PLUGIN NAME]
This will create a plugin project for the name you specify. Say for example you run
grails create-plugin example
. This would create a new plugin project called
example
.
The structure of a Grails plugin is exactly the same as a regular Grails project's directory structure, except that in the root of the plugin directory you will find a plugin Groovy file called the "plugin descriptor".
Being a regular Grails project has a number of benefits in that you can immediately get going testing your plugin by running:
The plugin descriptor itself ends with the convention
GrailsPlugin
and is found in the root of the plugin project. For example:
class ExampleGrailsPlugin {
def version = 0.1 …
}
All plugins must have this class in the root of their directory structure to be valid. The plugin class defines the version of the plugin and optionally various hooks into plugin extension points (covered shortly).
You can also provide additional information about your plugin using several special properties:
title
- short one sentence description of your plugin
version
- The version of your problem. Valid versions are for example "0.1", "0.2-SNAPSHOT", "0.1.4" etc.
grailsVersion
- The version of version range of Grails that the plugin supports. eg. "1.1 > *"
author
- plug-in author's name
authorEmail
- plug-in author's contact e-mail
description
- full multi-line description of plug-in's features
documentation
- URL where plug-in's documentation can be found
Here is an example from
Quartz Grails pluginclass QuartzGrailsPlugin {
def version = "0.1"
def grailsVersion = "1.1 > *"
def author = "Sergey Nebolsin"
def authorEmail = "nebolsin@gmail.com"
def title = "This plugin adds Quartz job scheduling features to Grails application."
def description = '''
Quartz plugin allows your Grails application to schedule jobs to be
executed using a specified interval or cron expression. The underlying
system uses the Quartz Enterprise Job Scheduler configured via Spring,
but is made simpler by the coding by convention paradigm.
'''
def documentation = "http://grails.org/Quartz+plugin" …
}
Installing & Distributing Plugins
To distribute a plugin you need to navigate to its root directory in a terminal window and then type:
This will create a zip file of the plugin starting with
grails-
then the plugin name and version. For example with the example plug-in created earlier this would be
grails-example-0.1.zip
. The
package-plugin
command will also generate
plugin.xml
file which contains machine-readable information about plugin's name, version, author, and so on.
Once you have a plugin distribution file you can navigate to a Grails project and type:
grails install-plugin /path/to/plugin/grails-example-0.1.zip
If the plugin is hosted on a remote HTTP server you can also do:
grails install-plugin http://myserver.com/plugins/grails-example-0.1.zip
Notes on excluded Artefacts
Although the
create-plugin command creates certain files for you so that the plug-in can be run as a Grails application, not all of these files are included when packaging a plug-in. The following is a list of artefacts created, but not included by
package-plugin:
grails-app/conf/DataSource.groovy
grails-app/conf/UrlMappings.groovy
build.xml
- Everything within
/web-app/WEB-INF
If you need artefacts within
WEB-INF
it is recommended you use the
_Install.groovy
script (covered later), which is executed when a plug-in is installed, to provide such artefacts. In addition, although
UrlMappings.groovy
is excluded you are allowed to include a
UrlMappings
definition with a different name, such as
FooUrlMappings.groovy
.
Distributing Plugins in Grails Plugins Repository
The preferred way of plugin distributing is to publish it under Grails Plugins Repository. This will make your plugin visible to the
list-plugins command:
Which lists all plugins in the Grails Plugin repository and also the
plugin-info command:
grails plugin-info [plugin-name]
Which outputs more information based on the meta info entered into the plug-in descriptor.
If you have created a Grails plug-in and want it to be hosted in the central repository take a look at the wiki page , which details how to go about releasing your plugin in the repository.
When you have access to the Grails Plug-in repository to release your plugin you simply have to execute the
release-plugin command:
This will automatically commit changes to SVN, do some tagging and make your changes available via the
list-plugins command.
Configuring Additional Repositories
By default when you use the
list-plugins,
install-plugin and
release-plugin command they work against the central repository hosted at http://plugins.grails.org.
However, Grails supports the notion of multiple plugin repositories. To configure multiple repositories you can using the
grails-app/conf/BuildConfig.groovy
file:
grails.plugin.repos.discovery.myRepository="http://svn.codehaus.org/grails/trunk/grails-test-plugin-repo"
grails.plugin.repos.distribution.myRepository="https://svn.codehaus.org/grails/trunk/grails-test-plugin-repo"
Repositories are split into those used for discovery over HTTP and those used for distribution, typically over HTTPS. You can also provide these settings in the
USER_HOME/.grails/settings.groovy
file if you prefer to share the same settings across multiple projects.
Once this is done the
list-plugins,
install-plugin and
plugin-info commands will automatically resolve against the newly configured repository. If you want to list only the plugins from the repository you can use its alias to do so:
grails list-plugins -repository=myRepository
Additionally, if you want to distribute a plugin within the configured repository you can do so with the
release-plugin command:
grails release-plugin -repository=myRepository
Secured Plugin Repositories
By default when using a repository URL starting with the
https://
protocol, Grails will prompt you for a username and password. If you prefer
not to get these prompts then you can specify the username and password to use in your
~/.grails/settings.groovy
file as follows:
grails.plugin.repos.discovery.myRepo="https://user01:password01@myserver.com"
The format is:
PROTOCOL://USERNAME:PASSWORD@SERVER
With this set Grails will use the specified username and password rather than prompting you for one.
Configuring Repository Search Order
A common use case for having your own plugin repository is when you want to override or provide a modified version of an existing plugin within the central repository.
However, by default Grails will search repository in a preset order. The repositories it will search are as follows:
default
- The default repository found at http://plugins.grails.org
core
- The repository for plugins provides by the framework (such as the hibernate plugin)
If you add an additional repository then Grails will search the repository you have defined last, meaning it is not possible to override a plugin in the central repository unless you change the search order. To change the repository search order you can do the following:
grails.plugin.repos.resolveOrder=['myRepository','default','core']
In the above case the repository called
myRepository
will be searched before the default one. In addition, if you remove the built in repositories from the list you can prevent Grails from searching these repositories at all:
grails.plugin.repos.resolveOrder=['myRepository']
This is useful in a circumstance where you don't want Grails to perform any internet lookups when searching for plugins.
As as mentioned previously, a plugin is merely a regular Grails application with a contained plug-in descriptors. However when installed, the structure of a plugin differs slightly. For example, take a look at this plugin directory structure:
+ grails-app
+ controllers
+ domain
+ taglib
etc.
+ lib
+ src
+ java
+ groovy
+ web-app
+ js
+ css
Essentially when a plugin is installed into a project, the contents of the
grails-app
directory will go into a directory such as
plugins/example-1.0/grails-app
. They
will not be copied into the main source tree. A plugin never interferes with a project's primary source tree.
Dealing with static resources is slightly different. When developing a plugin, just like an application, all static resources can go in the
web-app
directory. You can then link to static resources just like in an application (example below links to a javascript source):
<g:resource dir="js" file="mycode.js" />
When you run the plugin in development mode the link to the resource will resolve to something like
/js/mycode.js
. However, when the plugin is installed into an application the path will automatically change to something like
/plugin/example-0.1/js/mycode.js
and Grails will deal with making sure the resources are in the right place.
There is a special
pluginContextPath
variable that can be used whilst both developing the plugin and when in the plugin is installed into the application to find out what the correct path to the plugin is.
At runtime the
pluginContextPath
variable will either evaluate to an empty string or
/plugins/example
depending on whether the plugin is running standalone or has been installed in an application
Java & Groovy code that the plugin provides within the lib and
src/java
and
src/groovy
directories will be compiled into the main project's
web-app/WEB-INF/classes
directory so that they are made available at runtime.
Adding a new Script
A plugin can add a new script simply by providing the relevant Gant script within the scripts directory of the plugin:
+ MyPlugin.groovy
+ scripts <-- additional scripts here
+ grails-app
+ controllers
+ services
+ etc.
+ lib
Adding a new Controller, Tag Library or Service
A plugin can add a new controller, tag libraries, service or whatever by simply creating the relevant file within the
grails-app
tree. Note that when the plugin is installed it will be loaded from where it is installed and not copied into the main application tree.
+ ExamplePlugin.groovy
+ scripts
+ grails-app
+ controllers <-- additional controllers here
+ services <-- additional services here
+ etc. <-- additional XXX here
+ lib
Providing Views, Templates and View resolution
When a plug-in provides a controller it may also provide default views to be rendered. This is an excellent way to modularize your application through plugins. The way it works is that Grails' view resolution mechanism will first look the view in the application it is installed into and if that fails will attempt to look for the view within the plug-in.
For example given a
AmazonGrailsPlugin
plug-n provided controller called
BookController
if the action being executed is
list
, Grails will first look for a view called
grails-app/views/book/list.gsp
then if that fails will look for the same view relative to the plug-in.
Note however that if the view uses templates that are also provided by the plugin then the following syntax may be necessary:
<g:render template="fooTemplate" plugin="amazon"/>
Note the usage of the
plugin
attribute, which contains the name of the plugin where the template resides. If this is not specified then Grails will look for the template relative to the application.
Excluded Artefacts
Note that by default, when packaging a plug-in, Grails will excludes the following files from the packaged plug-in:
- grails-app/conf/DataSource.groovy
- grails-app/conf/UrlMappings.groovy
- Everything under web-app/WEB-INF
If your plug-in does require files under the
web-app/WEB-INF
directory it is recommended that you modify the plug-in's
scripts/_Install.groovy
Gant script to install these artefacts into the target project's directory tree.
In addition, the default
UrlMappings.groovy
file is excluded to avoid naming conflicts, however you are free to add a UrlMappings definition under a different name which
will be included. For example a file called
grails-app/conf/BlogUrlMappings.groovy
is fine.
Additionally the list of includes is extensible via the
pluginExcludes
property:
// resources that are excluded from plugin packaging
def pluginExcludes = [
"grails-app/views/error.gsp"
]
This is useful, for example, if you want to include demo or test resources in the plugin repository, but not include them in the final distribution.
Before moving onto looking at providing runtime configuration based on conventions you first need to understand how to evaluated those conventions from a plug-in. Essentially every plugin has an implicit
application
variable which is an instance of the
GrailsApplication interface.
The
GrailsApplication
interface provides methods to evaluate the conventions within the project and internally stores references to all classes within a GrailsApplication using the
GrailsClass interface.
A
GrailsClass
represents a physical Grails resources such as a controller or a tag library. For example to get all
GrailsClass
instances you can do:
application.allClasses.each { println it.name }
There are a few "magic" properties that the
GrailsApplication
instance possesses that allow you to narrow the type of artefact you are interested in. For example if you only want to controllers you can do:
application.controllerClasses.each { println it.name }
The dynamic method conventions are as follows:
*Classes
- Retrieves all the classes for a particular artefact name. Example application.controllerClasses
.
get*Class
- Retrieves a named class for a particular artefact. Example application.getControllerClass("ExampleController")
is*Class
- Returns true if the given class is of the given artefact type. Example application.isControllerClass(ExampleController.class)
The
GrailsClass
interface itself provides a number of useful methods that allow you to further evaluate and work with the conventions. These include:
getPropertyValue
- Gets the initial value of the given property on the class
hasProperty
- Returns true if the class has the specified property
newInstance
- Creates a new instance of this class.
getName
- Returns the logical name of the class in the application without the trailing convention part if applicable
getShortName
- Returns the short name of the class without package prefix
getFullName
- Returns the full name of the class in the application with the trailing convention part and with the package name
getPropertyName
- Returns the name of the class as a property name
getLogicalPropertyName
- Returns the logical property name of the class in the application without the trailing convention part if applicable
getNaturalName
- Returns the name of the property in natural terms (eg. 'lastName' becomes 'Last Name')
getPackageName
- Returns the package name
For a full reference refer to the
javadoc API.
Post-Install Configuration and Participating in Upgrades
Grails plug-ins can do post-install configuration and participate in application upgrade process (the
upgrade command). This is achieved via two specially named scripts under
scripts
directory of the plugin -
_Install.groovy
and
_Upgrade.groovy
.
_Install.groovy
is executed after the plugin has been installed and
_Upgrade.groovy
is executed each time the user upgrades his application with
upgrade command.
These scripts are normal
Gant scripts so you can use the full power of Gant. An addition to the standard Gant variables is the
pluginBasedir
variable which points at the plugin installation basedir.
As an example the below
_Install.groovy
script will create a new directory type under the
grails-app
directory and install a configuration template:
Ant.mkdir(dir:"${basedir}/grails-app/jobs")
Ant.copy(file:"${pluginBasedir}/src/samples/SamplePluginConfiguration.groovy",
todir:"${basedir}/grails-app/conf")// To access Grails home you can use following code:
// Ant.property(environment:"env")
// grailsHome = Ant.antProject.properties."env.GRAILS_HOME"
Scripting events
It is also possible to hook into command line scripting events through plug-ins. These are events triggered during execution of Grails target and plugin scripts.
For example, you can hook into status update output (i.e. "Tests passed", "Server running") and the creation of files or artefacts.
A plug-in merely has to provide an
_Events.groovy
script to listen to the required events. Refer the documentation on
Hooking into Events for further information.
Grails provides a number of hooks to leverage the different parts of the system and perform runtime configuration by convention.
Hooking into the Grails Spring configuration
First, you can hook in Grails runtime configuration by providing a property called
doWithSpring
which is assigned a block of code. For example the following snippet is from one of the core Grails plugins that provides
i18n support:
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;class I18nGrailsPlugin { def version = 0.1 def doWithSpring = {
messageSource(ReloadableResourceBundleMessageSource) {
basename = "WEB-INF/grails-app/i18n/messages"
}
localeChangeInterceptor(LocaleChangeInterceptor) {
paramName = "lang"
}
localeResolver(CookieLocaleResolver)
}
}
This plugin sets up the Grails
messageSource
bean and a couple of other beans to manage Locale resolution and switching. It using the
Spring Bean Builder syntax to do so.
Participating in web.xml Generation
Grails generates the
WEB-INF/web.xml
file at load time, and although plugins cannot change this file directly, they can participate in the generation of the file. Essentially a plugin can provide a
doWithWebDescriptor
property that is assigned a block of code that gets passed the
web.xml
as a
XmlSlurper
GPathResult
.
Consider the below example from the
ControllersPlugin
:
def doWithWebDescriptor = { webXml ->
def mappingElement = webXml.'servlet-mapping'
def lastMapping = mappingElement[mappingElement.size()-1]
lastMapping + {
'servlet-mapping' {
'servlet-name'("grails")
'url-pattern'("*.dispatch")
}
}
}
Here the plugin goes through gets a reference to the last
<servlet-mapping>
element and appends Grails' servlet to the end of it using XmlSlurper's ability to programmatically modify XML using closures and blocks.
Doing Post Initialisation Configuration
Sometimes it is useful to be able do some runtime configuration after the Spring
ApplicationContext has been built. In this case you can define a
doWithApplicationContext
closure property.
class SimplePlugin {
def name="simple"
def version = 1.1 def doWithApplicationContext = { appCtx ->
SessionFactory sf = appCtx.getBean("sessionFactory")
// do something here with session factory
}
}
The Basics
Grails plugins allow you to register dynamic methods with any Grails managed or other class at runtime. New methods can only be added within a
doWithDynamicMethods
closure of a plugin.
For Grails managed classes like controllers, tag libraries and so forth you can add methods, constructors etc. using the
ExpandoMetaClass mechanism by accessing each controller's
MetaClass:
class ExamplePlugin {
def doWithDynamicMethods = { applicationContext ->
application.controllerClasses.each { controllerClass ->
controllerClass.metaClass.myNewMethod = {-> println "hello world" }
}
}
}
In this case we use the implicit application object to get a reference to all of the controller classes' MetaClass instances and then add a new method called
myNewMethod
to each controller.
Alternatively, if you know before hand the class you wish the add a method to you can simple reference that classes
metaClass
property:
class ExamplePlugin { def doWithDynamicMethods = { applicationContext ->
String.metaClass.swapCase = {->
def sb = new StringBuffer()
delegate.each {
sb << (Character.isUpperCase(it as char) ?
Character.toLowerCase(it as char) :
Character.toUpperCase(it as char))
}
sb.toString()
} assert "UpAndDown" == "uPaNDdOWN".swapCase()
}
}
In this example we add a new method
swapCase
to
java.lang.String
directly by accessing its
metaClass
.
Interacting with the ApplicationContext
The
doWithDynamicMethods
closure gets passed the Spring
ApplicationContext
instance. This is useful as it allows you to interact with objects within it. For example if you where implementing a method to interact with Hibernate you could use the
SessionFactory
instance in combination with a
HibernateTemplate
:
import org.springframework.orm.hibernate3.HibernateTemplateclass ExampleHibernatePlugin { def doWithDynamicMethods = { applicationContext -> application.domainClasses.each { domainClass -> domainClass.metaClass.static.load = { Long id->
def sf = applicationContext.sessionFactory
def template = new HibernateTemplate(sf)
template.load(delegate, id)
}
}
}
}
Also because of the autowiring and dependency injection capability of the Spring container you can implement more powerful dynamic constructors that use the application context to wire dependencies into your object at runtime:
class MyConstructorPlugin { def doWithDynamicMethods = { applicationContext ->
application.domainClasses.each { domainClass ->
domainClass.metaClass.constructor = {->
return applicationContext.getBean(domainClass.name)
}
} }
}
Here we actually replace the default constructor with one that looks up prototyped Spring beans instead!
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.
Controlling Plug-in Dependencies
Plug-ins often depend on the presence of other plugins and can also adapt depending on the presence of others. To cover this, a plugin can define two properties. The first is called
dependsOn
. For example, take a look at this snippet from the Grails Hibernate plugin:
class HibernateGrailsPlugin {
def version = 1.0
def dependsOn = [dataSource:1.0,
domainClass:1.0,
i18n:1.0,
core: 1.0]}
As the above example demonstrates the Hibernate plugin is dependent on the presence of 4 plugins: The
dataSource
plugin, The
domainClass
plugin, the
i18n
plugin and the
core
plugin.
Essentially the dependencies will be loaded first and then the Hibernate plugin. If all dependencies do not load, then the plugin will not load.
The
dependsOn
property also supports a mini expression language for specifying version ranges. A few examples of the syntax can be seen below:
def dependsOn = [foo:"* > 1.0"]
def dependsOn = [foo:"1.0 > 1.1"]
def dependsOn = [foo:"1.0 > *"]
When the wildcard * character is used it denotes "any" version. The expression syntax also excludes any suffixes such as -BETA, -ALPHA etc. so for example the expression "1.0 > 1.1" would match any of the following versions:
- 1.1
- 1.0
- 1.0.1
- 1.0.3-SNAPSHOT
- 1.1-BETA2
Controlling Load Order
Using
dependsOn
establishes a "hard" dependency in that if the dependency is not resolved, the plugin will give up and won't load. It is possible though to have a "weaker" dependency using the
loadAfter
property:
def loadAfter = ['controllers']
Here the plugin will be loaded after the
controllers
plugin if it exists, otherwise it will just be loaded. The plugin can then adapt to the presence of the other plugin, for example the Hibernate plugin has this code in the
doWithSpring
closure:
if(manager?.hasGrailsPlugin("controllers")) {
openSessionInViewInterceptor(OpenSessionInViewInterceptor) {
flushMode = HibernateAccessor.FLUSH_MANUAL
sessionFactory = sessionFactory
}
grailsUrlHandlerMapping.interceptors << openSessionInViewInterceptor
}
Here the Hibernate plugin will only register an
OpenSessionInViewInterceptor
if the
controllers
plugin has been loaded. The manager variable is an instance of the
GrailsPluginManager interface and it provides methods to interact with other plugins and the
GrailsPluginManager
itself from any plugin.