Simple plugin system

Gaelyk supports a plugin system which helps you modularize your applications and enable you to share commonalities between Gaelyk applications.

This page is about creating new plugins. The list of existing plugins can be found in the Plugins section.

What a plugin can do for you

A plugin lets you:

Possible examples of plugins can:

Anatomy of a Gaelyk plugin

Binary plugins

Binary plugin is a JAR file added to your WEB-INF/lib folder or more preferable declared as an dependency in your build file e.g. the Gradle one shipped with the template project so the build system can handle dependencies for you. Binary plugins can add groovlets, templates and even static content (with the help of the resources plugin) to your Gaelyk application without cluttering it with many additional files. Binary plugins are installed automatically, there is no other action needed to start using them. They can be removed easily as well. You just delete the JAR file from the WEB-INF/lib folder or remove the dependency from your build file.

To let Gaelyk application to recognize your binary plugin, you'll have to create a plugin descriptor which will allow you to define new binding variables, new routes, and any initialization code your plugin may need on application startup. This plugin descriptor must extend groovyx.gaelyk.plugins.PluginBaseScript manually. Place your plugin descriptor's code inside the method run.

Binary plugins are resolved using Java's ServiceLoader so you also need to place binary name of your plugin descriptor e.g. com.example.plugin.ExamplePlugin in META-INF/services/groovyx.gaelyk.plugins.PluginBaseScript file inside your JAR. If you are not using templates you can just place your groovlets into your Groovy source folder to let the compiler compile them. Otherwise you will need Gradle Gaelyk Plugin v.0.3.1 and higher to help you package the plugin. As long as your structure follow Gaelyk convetions you only need to call jar task of the plugin. You may also need to enable the jar task placing jar.enabled=true into your build file.

Hierarchy

Note:Don't forget to declare packages for Groovlets otherwise they will be placed in wrong destination folders!

Using standard Gradle layout, your plugin project should look like:


src/main                                                // Gradle main sources set
 |
 +-- groovy                                             // groovy source folder
 |    |
 |    +-- com/example/plugin                            // plugin package
 |         |
 |         +-- myGroovletInSrc.groovy                   // groovlet placed in source folder
 |         | 
 |         +-- ExamplePlugin.groovy                     // plugin descriptor
 |
 +-- resources                                          // resources folder
 |    |
 |    +-- META-INF/services                             // services folder used by ServiceLoader
 |    |    |
 |    |    +-- groovyx.gaelyk.plugins.PluginBaseScript  // service implementation descriptor with
 |    |                                                 // single line com.example.plugin.ExamplePlugin
 |    |
 |    +-- resources                                     // your static resources (requires resources plugin)  
 |         |
 |         +-- main.css
 |
 +-- webapp                                             // web application folder
     |
     +-- com/example/plugin                             // package-like folder to prevent name clash
     |    |
     |    +-- myTemlate.gtpl                            // your Groovy template
     |
     +-- WEB-INF/groovy                                 // your regular groovlets
          |
          +-- com/example/plugin                        // use packages to prevent name clash
               |
               +-- myRegularGroovlet.groovy             // your groovlet

The same project packaged will look like:


plugin.jar                                              // JAR file
 |
 +-- com/example/plugin                                 // your plugin package
 |    |
 |    +-- myGroovletInSrc.class                         // compiled groovlet
 |    |
 |    +-- myRegularGroovlet.class                       // compiled groovlet
 |    |
 |    +-- $gtpl$myTemplate.class                        // template compiled with $gtpl$prefix
 |    | 
 |    +-- ExamplePlugin.class                           // compiled plugin descriptor
 |
 +-- META-INF/services                                  // services folder used by ServiceLoader
 |    |
 |    +-- groovyx.gaelyk.plugins.PluginBaseScript       // service implementation descriptor with
 |                                                      // single line com.example.plugin.ExamplePlugin
 |
 +-- resources                                          // your bundled static resources 
      |
      +-- main.css


Exploded plugins

Exploded plugin is actually just some content you'll drop in your war/ folder, at the root of your Gaelyk application! This is why you can add all kind of static content, as well as groovlets and templates, or additional JARs in WEB-INF/lib. Furthermore, plugins don't even need to be external plugins that you install in your applications, but you can just customize your application by using the conventions and capabilities offered by the plugin system. Then, you really just need to have /WEB-INF/plugins.groovy referencing /WEB-INF/plugins/myPluginDescriptor.groovy, your plugin descriptor.

To let Gaelyk application to recognize your exploded plugin, you'll have to create a plugin descriptor in WEB-INF/plugins. Also, this plugin descriptor script should be referenced in the plugins.groovy script in WEB-INF/

Hierarchy

The content of exploded plugin would look something like the following hierarchy:

/
+-- war
    |
    +-- someTemplate.gtpl                       // your templates
    |
    +-- css
    +-- images                                  // your static content
    +-- js
    |
    +-- WEB-INF
        |
        +-- plugins.groovy                      // the list of plugins
        |                                       // descriptors to be installed
        +-- plugins
        |   |
        |   +-- myPluginDescriptor.groovy       // your plugin descriptor
        |
        +-- groovy
        |    |
        |    +-- myGroovlet.groovy              // your groovlets
        |
        +-- includes
        |    |
        |    +-- someInclude.gtpl               // your includes
        |
        +-- classes                             // compiled classes
        |
        +-- lib
            |
            +-- my-additional-dependency.jar    // your JARs

The bare minimum to have an exploded plugin in your application is to have a plugin descriptor, like /WEB-INF/plugins/myPluginDescriptor.groovy in this example, that is referenced in /WEB-INF/plugins.groovy.

Developing a plugin is just like developing a normal Gaelyk web application. Follow the usual conventions and describe your plugin in the plugin descriptor. Then afterwards, package it, share it, and install it in your applications.

The plugin descriptor

The plugin descriptor is where you'll be able to tell the Gaelyk runtime to:

Here's what a plugin descriptor can look like:

// add imports you need in your descriptor
import net.sf.json.*
import net.sf.json.groovy.*

// add new variables in the binding
binding {
    // a simple string variable
    jsonLibVersion = "2.3"
    // an instance of a class of a third-party JAR
	json = new JsonGroovyBuilder()
}

// add new routes with the usual routing system format
routes {
    // always specify the first route index to prevent routes conflict
    // by setting the value to negative number the routes from this plugin
    // will have precedence before the ones in routes.groovy script
    startRoutingAt -1500
    get "/json", forward: "/json.groovy"
}

before {
    log.info "Visiting ${request.requestURI}"
    binding.uri = request.requestURI
    request.message = "Hello"
}

after {
    log.info "Exiting ${request.requestURI}"
    log.info "groovlet returned $result from its exection"
}

// any other initialization code you'd need
// ...

All of the GAE specific variables are available in plugin descriptors as implicit variables. You can also access ServletContext instance of your application using the servletContext implicit variable.

Inside the binding closure block, you just assign a value to a variable. And this variable will actually be available within your groovlets and templates as implicit variables. So you can reference them with ${myVar}} in a template, or use myVar directly inside a groovlet, without having to declare or retrieve it in any way.

Note: a plugin may overwrite the default Gaelyk variable binding, or variable bindings defined by the previous plugin in the initialization chain. In the plugin usage section, you'll learn how to influence the order of loading of plugins.
Inside the routes closure block, you'll put the URL routes following the same syntax as the one we explained in the URL routing section.
Note: Contrary to binding variables, the first route that matches is the one which is chosen. This means a plugin cannot overwrite the existing application routes, or routes defined by previous plugins in the chain.
Important: If your plugins contribute routes, make sure your application has also configured the routes filter, as well as defined a WEB-INF/routes.groovy script, otherwise no plugin routes will be present.

In the before and after blocks, you can access the request, response, log, and binding variables. The logger name is of the form gaelyk.plugins.myPluginName. The binding variables allows you to update the variables that are put in the binding of Groovlets and templates. In after block you can access the result of groovlet execution as result variable.

Wherever in your plugin descriptor, you can put any initialization code you may need in your plugin.

Important: The plugins are loaded once, as soon as the first request is served. So your initialization code, adding binding variables and routes, will only be done once per application load. Knowing that Google App Engine can load and unload apps depending on traffic, this is important to keep in mind as well.

Using a plugin

This section applies only on exploded plugins. Binary plugins are installed automatically.

If you recall, we mentioned the plugins.groovy script. This is a new script since Gaelyk 0.4, that lives alongside the routes.groovy script (if you have one) in /WEB-INF. If you don't have a plugins.groovy script, obviously, no exploded plugin will be installed — or at least none of the initialization and configuration done in the various plugin descriptors will ever get run.

This plugins.groovy configuration file just lists the plugins you have installed and want to use. An example will illustrate how you reference a plugin:

install jsonPlugin
Note: For each plugin, you'll have an install method call, taking as parameter the name of the plugin. This name is actually just the plugin descriptor script name. In this example, this means Gaelyk will load WEB-INF/plugins/jsonPlugin.groovy.

As mentioned previously while talking about the precedence rules, the order with which the plugins are loaded may have an impact on your application or other plugins previously installed and initialized. But hopefully, such conflicts shouldn't happen too often, and this should be resolved easily, as you have full control over the code you're installing through these plugins to make the necessary amendments should there be any.

When you are using two plugins with before / after request actions, the order of execution of these actions also depends on the order in which you installed your plugins. For example, if you have installed pluginOne first and pluginTwo second, here's the order of execution of the actions and of the Groovlet or template:

How to distribute and deploy a plugin

The best way to distribute your binary plugin is through the Maven Central repository. You can use Sonatype OSS repository to get your plugins there.

If you want to share an explodeded plugin you've worked on, you just need to zip everything that constitutes the plugin. Then you can share this zip, and someone who wishes to install it on his application will just need to unzip it and pickup the various files of that archive and stick them up in the appropriate directories in his/her Gaelyk war/ folder, and reference that plugin, as explained in the previous section.

The best way how to share your exploded plugin is by using the plugin catalogue. Fill the form and wait until the plugin is approved.