14 Plugins - Reference Documentation
Authors: Andres Almiray
Version: 1.2.0
Table of Contents
14 Plugins
Griffon 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.14.1 Creating and Installing Plugins
Creating Plug-ins
Creating a Griffon plugin is a simple matter of running the command:griffon create-plugin [PLUGIN NAME]
griffon create-plugin example
. This would create a new plugin project called example
.The structure of a Griffon plugin is exactly the same as a regular Griffon project's directory structure, except that in the root of the plugin directory you will find a plugin Groovy file called the "plugin descriptor".The plugin descriptor itself ends with the convention GriffonPlugin
and is found in the root of the plugin project. For example:class ExampleGriffonPlugin { def version = 0.1 … }
title
- short one sentence description of your pluginversion
- the version of your plugin. Valid versions are for example "0.1", "0.2-SNAPSHOT", "0.1.4" etc.griffonVersion
- The version of version range of Griffon that the plugin supports. eg. "1.1 > *"license
- the plugin's license name in one sentencepluginIncludes
- additional resources that should be included in the plugin ziptoolkits
- a list of supported toolkits [swing, javafx, swt, pivot, gtk]platforms
- a list of supported platforms [linux, linux64, windows, windows64, macosx, macosx64, solaris, solaris64]authosr
- a list of plugin author names/emailsdescription
- full multi-line description of plugin's featuresdocumentation
- URL where plugin's documentation can be foundsource
- URL where plugin's source can be foundframework
- a boolean indicating if this plugin can be installed at the framework level, such as the Git plugin.
class SwingGriffonPlugin { String version = '0.9.5' String griffonVersion = '0.9.5 > *' Map dependsOn = [:] List pluginIncludes = [] String license = 'Apache Software License 2.0' List toolkits = ['swing'] List platforms = [] String documentation = '' String source = 'https://github.com/griffon/griffon-swing-plugin' List authors = [ [ name: 'Andres Almiray', email: 'aalmiray@yahoo.com' ] ] String title = 'Enables Swing support' String description = ''' Enables the usage of Swing based components in Views.Usage ---- This plugin enables the usage of the following nodes inside a View....Configuration ------------- There's no special configuration for this plugin.[1]: http://groovy.codehaus.org/Swing+Builder ''' }
Installing & Distributing Plugins
To distribute a plugin you need to navigate to its root directory in a terminal window and then type:griffon package-plugin
griffon-
then the plugin name and version. For example with the example plugin created earlier this would be griffon-example-0.1.zip
. The package-plugin
command will also generate plugin.json
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 Griffon project and type:griffon install-plugin /path/to/plugin/griffon-example-0.1.zip
griffon install-plugin http://myserver.com/plugins/griffon-example-0.1.zip
Releasing Plugins into a Griffon Artifact Repository
To release a plugin call therelease-plugin
command while inside the plugin project. If no repository
flag is specified then the default artifact repository (griffon-central
) will be used. For quick testing purposes you can publish a release to griffon-local
(which is always available) by issuing the following commandgriffon install-plugin --repository=griffon-local
package-plugin
to package-archetype
; from install-plugin
to install-archetype
; from release-plugin
to release-archetype
.Should you decide to become a plugin/archetype author and wish to publish your artifacts to the Griffon Central repository then you must follow these steps:
- Create an account at http://artifacts.griffon-framework.org
- After confirming your email, log into your profile and click the button for membership request.
- Ping us at the developer mailing list or at @theaviary
- Once approved configure your credentials in
$USER_HOME/.griffon/settings.groovy
like this
griffon.artifact.repositories = [ 'griffon-central': [ username: 'yourUsername', password: 'yourPassword' ] ]
14.2 Artifact Repositories
There are 3 types of plugin repositories:local
, remote
and legacy
. Artifact repositories can be either configured locally to a project (inside griffon-app/conf/BuildConfig
) or globally to all projects (inside $USER_HOME/.griffon/settings.groovy
),Local Artifact Repositories
This type of repository is file based and can be hosted anywhere in the file system, even on shared folders over the network. Local repositories makes it easier to share snapshot releases among team mates as the network latency should be smaller. Their configuration requires but one parameter to be specified: the path where the artifacts will be placed. Here's a sample configuration for a local repository named 'my-local-repo
'.griffon.artifact.repositories = [ 'my-local-repo': [ type: 'local', path: '/usr/local/share/griffon/repository' ] ]
griffon-local
' and it's default path is $USER_HOME/.griffon/repository
. This repository is the default place where downloaded plugins will be installed for speeding up retrievals at a later time.Remote Artifact Repositories
This type of repository allows developers to publish releases via SCP or web. The repository is handled by a Grails application whose code is freely available at https://github.com/griffon/griffon-artifact-portal .This code has been released under Apache Software License 2.0. Follow the instructions found in the README to run your own artifact portal. Configuring a remote repository requires a different set of properties than those exposed by local repositories. For example, if your organization is running a remote artifact repository located athttp://acme.com:8080/portal
then use the following configurationgriffon.artifact.repositories = [ 'acme': [ type: 'remote', url: 'http://acme.com:8080/portal' ] ]
griffon.artifact.repositories = [ 'acme': [ type: 'remote', url: 'http://acme.com:8080/portal', username: 'wallace', password: 'gromit', port: 2345, timeout: 60 ] ]
- port = 2222
- timeout = 30 (in seconds)
username
and password
out however you will be asked for this credentials when publishing a release to this particular repository. Adding your credentials in the configuration avoids typing them when releasing artifacts.Legacy Artifact Repository
This is a very special type of repository that exists only for backward compatibility during the migration of the old Griffon plugin repository to the new infrastructure in http://artifacts.griffon-framework.org .There are no configuration options for this repository, neither you can publish a release to it; it's effectively read-only.14.3 Understanding a Plugins Structure
As mentioned previously, a plugin is merely a project with an structure similar to a Griffon application with the addition of a contained plugin descriptor. However when installed, the structure of a plugin differs slightly. For example, take a look at this plugin directory structure:+ griffon-app + controllers + models + views … + lib + src + main + cli + doc
plugins/example-1.0/
. Plugin contents will not be copied into the main source tree. A plugin never interferes with a project's primary source tree.
14.4 Providing Basic Artefacts
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 + griffon-app + controllers + models + etc. + lib
Adding a new Controller, Model, View or Service
A plugin can add a new MVC Group, service or whatever by simply creating the relevant file within thegriffon-app
tree. However you'll need to create an Addon in order to package them properly.+ ExamplePlugin.groovy + scripts + griffon-app + controllers <-- additional controllers here + services <-- additional services here + etc. <-- additional XXX here + lib
14.5 Hooking into Build Events
Post-Install Configuration and Participating in Upgrades
Griffon plugins can do post-install configuration and participate in application upgrade process (the upgrade command). This is achieved via two specially named scripts underscripts
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 griffon-app
directory and install a configuration template:ant.mkdir(dir:"${basedir}/griffon-app/jobs") ant.copy(file:"${pluginBasedir}/src/samples/SamplePluginConfiguration.groovy", todir:"${basedir}/griffon-app/conf")// To access Griffon home you can use following code: // ant.property(environment:"env") // griffonHome = ant.antProject.properties."env.GRIFFON_HOME"
Scripting events
It is also possible to hook into command line scripting events through plugins. These are events triggered during execution of Griffon 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 artifacts.A plugin merely has to provide an_Events.groovy
script to listen to the required events. Refer the documentation on Hooking into Events for further information.
14.6 Addons
Understanding Addons
Addons are a plugin's best friend. While plugins can only contribute build-time artifacts (such as scripts) and participate on build events, addons may contribute runtime artifacts (such as MVC Groups or services) and participate on application events.Often times whenever you'd like to package a reusable runtime artifact you'd have to create an Addon as well.Addon responsibilities
Addons may contribute any of the following to your application: MVC Groups and application event handlers. They can also contribute the following to the CompositeBuilder: factories, methods, properties and FactoryBuilderSupport delegates (attribute, preInstantiate, postInstantiate, postNodeCompletion).Addons are created using a template that suggests all of the properties and methods you can use configure. The complete list follows:addonInit
- called right after the addon has been loaded but before contributions are taken into accountaddonPostInit
- called after all contributions haven been madeaddonBuilderInit
- called before contributions to the CompositeBuilder are taken into accountaddonBuilderPostInit
- called after all CompositeBuilder contributions haven been madeevents
- Map of additional application event handlersfactories
- Map of additional node factories, added to CompositeBuildermethods
- Map of additional methods, added to CompositeBuilderprops
- Map of additional methods, added to CompositeBuilderattributeDelegates
- List of attributeDelegates (as Closures), added to CompositeBuilderpreInstantiateDelegates
- List of preInstantiateDelegates (as Closures), added to CompositeBuilderpostInstantiateDelegates
- List of postInstantiateDelegates (as Closures), added to CompositeBuilderpostNodeCompletionDelegates
- List of postNodeCompletionDelegates (as Closures), added to CompositeBuilder
Configuring Addons
This task is done automatically for you when you package an addon inside a plugin. The plugin's_Install
and _Uninstall
scripts will make sure that griffon-app/conf/Builder.groovy
stays up to date. When you install a plugin that contains an addon you'll notice that Builder.groovy
may get updated with a line similar to the next oneroot.'CustomGriffonAddon'.addon=true
Builder.groovy
file. You can still apply modifications as explained below.The following snippet shows how to configure an Addon to contribute all of its methods to Controllers, and all of its contributions to Models.root.'CustomGriffonAddon'.controller='*:methods' root.'CustomGriffonAddon'.model='*'
root
) of the addon to something more suitable to your needs. All nodes contributed by the addon will now be accessible using that prefix. Here's an examplenx.'CustomGriffonAddon'.addon=true
CustomGriffonAddon
is defined as followsclass CustomGriffonAddon { def factories = [ button: com.acme.CustomButton ] }
CustomButtom
can be obtained by using nxbutton
, whereas regular instances of JButton
will be accessible with button
.
14.7 Understanding Plugin Order
Controlling Plugin Dependencies
Plugins 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 adependsOn
property. For example, take a look at this snippet from the Griffon Clojure plugin:class ClojureGriffonPlugin { String version = '0.9' String dependsOn = ['lang-bridge': '0.5']}
lang-bridge
plugin.Essentially the dependencies will be loaded first and then the Clojure 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 > *"]
- 1.1
- 1.0
- 1.0.1
- 1.0.3-SNAPSHOT
- 1.1-BETA2
Controlling Addon Load Order
Addons will be loaded in the order determined by the dependencies set forth in their containing plugins. UsingdependsOn
establishes a "hard" dependency. Any addons provided by the dependencies will be added first to the builder configuration file when installed.
14.8 CLI Dependencies
Plugins can provide compile time classes that should not be bundled with runtime classes (i.e, addon sources). Sources and resources placed under$basedir/src/cli
will be automatically compiled and packaged into a jar whose name matches griffon-${plugin.name}-compile-${plugin.version}.jar
. A typical use case for these type of classes is a custom AST transformation that should be run during compile time but not at runtime.