Experiences with Publishing a Grails Plugin
I’ve really enjoyed working in the Grails ecosystem. There are a variety of reasons of which I won’t write in this post. I am going to focus on one and that is the plugin system. Plugins provide a great way to re-use capability easily in a way I don’t recall seeing elsewhere in the Java ecosystem. I recently authored my first Grails plugin and I want to share my experiences here.
The code for the plugin is at http://github.com/double16/grails-ziplet. The plugin is registered at http://grails.org/plugin/ziplet.
Grails Plugin Advantages
Grails plugins provide more than Java or Groovy code like a Maven or Ivy. They can provide user interfaces, JavaScript, stylesheets, configurations and more without the developer needing to copy files into the project or update the web.xml, Spring config, etc. Note that some plugins do require additional configuration in Config.groovy, Spring beans in resources.groovy, etc., but these changes generally do not interfere with the project itself.
Web Site Performance
The plugin improves the performance of the web application by compressing the controller response via HTTP GZIP compression. One of the best practices for web site performance is to reduce network bandwidth. Compression is an easy win because HTML, JSON, XML, CSS and JavaScript compress well, usually reducing the size by 80-90%. Other resources such as images are usually already compressed and should not add additional compression over the HTTP transport.
Several plugins already provide for compression of non-controller output, such as CSS and JavaScript. Most commonly the asset-pipeline and resources plugins. Controller output is
not compressed with these plugins. Typically a web application container is fronted by an HTTP proxy, such as Apache. In this case, the proxy should be configured to compress output because it can be done more efficiently. This isn’t always possible in the deployment architecture. There may be no HTTP proxy in front of the web application container or in a PaaS environment the configuration may not be available to the owner of the web app. Thus it falls on the web application to do the compression.
There is an existing Java EE filter implementation that provides output compression that is mature and robust. It is called ziplet and is available on GitHub. The ziplet plugin integrates this filter into Grails.
Starting a Plugin
Grails provides a command for creating a new plugin called create-plugin, and you give it the name of your new plugin. A new directory with the plugin name will be created, and then all the files under that. So don’t create a directory named “ziplet”, cd to that, and then
enter the command. Let Grails create the “ziplet” folder for you.
<br />
$ grails create-plugin ziplet<br />
I won’t go through all the details of creating the plugin, the Grails documentation covers a lot. Pay particular attention to the plugin script, in this case it is ZipletGrailsPlugin.groovy
. Make sure all applicable fields are filled out, comments and unused fields are removed. Make sure to fill out the documentation, source and issues links. These are shown on the plugins site and it’s annoying to click the buttons and go no where.
After my plugin was submitted, Burt Beckwith created a pull request to cleanup a bunch of stuff. One of my favorite things about collaboration is learning from others. I recommend reviewing the pull request to see what files can be left out of the plugin and how things are to look. I’ve seen quite a few projects with generated files hanging around and wasn’t sure what was safe to delete.
Implementation
The ziplet plugin brings in the ziplet filter dependency and configures the web.xml file. Grails makes this straight-forward and allows for other plugins and the project to add to the web.xml without error prone parsing.
Dependencies
Including a dependency is the same as doing it in the project. Add the dependency to BuildConfig.groovy:
dependencies {<br />
compile('com.github.ziplet:ziplet:2.0.0') {<br />
excludes 'slf4j-nop'<br />
}<br />
}<br />
web.xml
Java EE filters require filter and filter-mapping elements. There is also configuration needed in the filter. This was the most complicated part of the plugin. It looks something like:
<br />
def contextParam = xml.'context-param'<br />
contextParam[contextParam.size() - 1] + {<br />
filter {<br />
'filter-name'('CompressingFilter')<br />
'filter-class'(CompressingFilter.name)<br />
if (config.debug) {<br />
'init-param' {<br />
'param-name'('debug')<br />
'param-value'('true')<br />
}<br />
}<br />
if (config.compressionThreshold) {<br />
'init-param' {<br />
'param-name'('compressionThreshold')<br />
'param-value'(config.compressionThreshold)<br />
}<br />
}<br />
...<br />
}<br />
Testing
Grails has excellent support for testing: unit, integration and functional. However, I found the plugin script difficult to test. It is not in the classpath for unit testing nor integration testing. Functional testing is too heavy for most code in the plugin script, and especially so
for this case. I solved the problem by moving as much code as possible to a helper class in src/groovy
.
ZipletGrailsPlugin.groovy:<br />
…<br />
def doWithWebDescriptor = { xml -><br />
new WebXmlHelper().updateWebXml(application, xml)<br />
}<br />
…<br />
The plugin implementation is about configuring the web.xml, so all of the tests deal with checking that the web.xml was modified correctly. The plugin script uses XmlSlurper to modify the XML, so we need to do the same thing in the test. Also, assertions were difficult because modifying the XmlSlurper didn’t allow for checking the result, so I needed to re-parse the XML.
<br />
// xml is an XmlSlurper object<br />
String str = new StreamingMarkupBuilder().bindNode(xml)<br />
xml = new XmlSlurper().parseText(str)<br />
The complete code is at https://github.com/double16/grails-ziplet/blob/master/test/unit/grails/plugin/ziplet/WebXmlHelperSpec.groovy.
Publishing
Publishing a plugin is simple, the documentation is good. It took all of three days to get my request approved, fairly quick. I recommend reading the comments on pending plugins to learn anything you might need to do.
After your plugin is approved you’re not done. You need to login to grails.org, find your plugin, and edit it. Add some tags so your plugin will be categorized correctly. Refer to similar plugins for tags. In the ziplet case, I added “utility” and “performance”. I edited the install instructions because the version was “null”. Unfortunately this doesn’t track your releases, so you need to update for each release. I’ve learned when including a plugin to look at the top for the latest release instead of the install instructions, so it’s probably not a big deal if you don’t update this with each release.
Some plugins copy the full description, such as the README.md, into this page. The Documentation button will take users there, so I figured that’s better than having to modify the plugin page for each release.
Conclusion
Grails plugins are fantastic. They are simple to use and relatively painless to create and publish. I recommend developers consider contributing re-usable code using the plugin system. You’ll give back to the open source community and likely get others to improve your plugin, and thus improving your project.