Thursday, November 05, 2009

Griffon Plugins and Addons

In this post, I'm going to talk a bit about Griffon plugins and addons--what they are, why they're useful, and how you can create your own.  As an example, I'll walk through the steps used to create the recently released Griffon Mail Plugin.

What are Plugins and Addons?

  • Plugins provide a mechanism to extend your Griffon application with new functionality at build-time.  They are commonly used to integrate external libraries, add new builders, and support testing tools.  The full list of Griffon plugins is available here.
  • Addons are a new feature in Griffon 0.2 that provide mechanism to extend your Griffon application with new functionality at run-time.  Addons can add items to builders, add MVC groups, and add/respond to runtime events.  An example of this is the Guice plugin, which also bundles an addon that starts up the Guice framework and injects services into your controllers as they are created.  Another example is the REST plugin which uses and addon to add new methods to controllers as they are created.
Plugins and Addons are conceptually very similar.  Both are about adding new functionality and you will often see plugins and addons on working hand in hand.

Creating your own Plugins and Addons
Creating your own Griffon plugins and addons is not too difficult.  We'll walk through the process by re-creating the Griffon mail plugin.  So without further ado, lets get started:

Step 1: Create the plugin
griffon create-plugin mail

Step 2: Edit the plugin metadata
cd mail
edit MailGriffonPlugin.groovy which Griffon created for you:
class MailGriffonPlugin {
    def version = 0.1
    def canBeGlobal = false
    def dependsOn = [:]

    // TODO Fill in your own info
    def author = "Josh Reed"
    def authorEmail = "jareed@andrill.org"
    def title = "Send email from your Griffon app"
    def description = 'Send email from your Griffon app'

    // URL to the plugin's documentation
    def documentation = "http://griffon.codehaus.org/Mail+Plugin"
}

Step 3: Add our plugin functionality
What you do here will depend on your plugin, but in this case we need to add the mail.jar file from the JavaMail API site to the plugins lib/ directory.  When the user installs our plugin, this jar file will be added to the application's classpath.

The JavaMail API requires a bit of work to send emails, so we'll also add a helper class that hides this code behind a single static method call.

Step 4: Package your plugin so you can test it
When you're done with your plugin, you will want to test it out.  To do this:
griffon package-plugin
This will compile your plugin and package it as a zip file called griffon-mail-0.1.zip.  You can then install this plugin in a regular Griffon application with a:
griffon install-plugin griffon-mail-0.1.zip

For the mail plugin, we can send an email by calling our helper class:
MailService.sendMail(mailhost: 'smtp.server.com', to: 'jareed@andrill.org'...)
from our controller or wherever.  This is OK but we can do one better by creating an Addon that injects the sendMail method into every controller.

Step 5: Creating an Addon
Head back to our plugin directory and issue a:
griffon create-addon mail
this will create a MailGriffonAddon.groovy file.
(Note: if you try this with 0.2 you may run into an error about 'createMVC'  This is a bug that has been fixed, but you can fix it yourself by editing your $GRIFFON_HOME/scripts/CreateAddon.groovy file.  The last line should say 'createAddon' instead of 'createMVC')

This is where we do our runtime magic:
import griffon.util.IGriffonApplication

class MailGriffonAddon {
    private IGriffonApplication application
 
    // add ourselves as a listener so we can respond to events
    def addonInit = { app ->
        application = app
        app.addApplicationEventListener(this)
    }
 
    // inject the 'sendMail' into our 
    def onNewInstance = { klass, type, instance ->
        def types = application.config.griffon?.mail?.injectInto ?: ["controller"]
        if (!types.contains(type)) return
        instance.metaClass.sendMail = { Map args -> MailService.sendMail(args)}
    }
}

With this done, we can: griffon package-plugin and install the plugin into our test app: griffon install-plugin griffon-mail-0.1.zip
With the addon in place, we can reference the sendMail() method directly on our controller.

Step 6: Release the plugin
If you have write access to the Griffon Subversion, you can release your plugin with a:
griffon release-plugin
If you don't have write access, ask on the griffon-dev list and someone can help you out.

I hope this demystifies the plugin and addon building process.