Showing posts with label plugin. Show all posts
Showing posts with label plugin. Show all posts

Wednesday, August 12, 2009

Griffon Installer Plugin

Last week Andres Almiray and I released a new version of the Griffon Installer plugin. This version adds support for creating native Mac app bundles for your Griffon application, as well a few refactorings to make the generated artifacts more consistent. In this post, I'm going to walk you through the process of creating a set of native launchers for your application. A future post will explain the process of creating installers.

Getting started using the installer plugin is simple:
griffon install-plugin installer

This will fetch the plugin and install it into your Griffon app. The plugin adds several new targets. All of the launcher-related targets are broken into two steps: a prepare step that copies all of the configuration files for that particular launcher into your application, and a create step that actually generates the native launcher. The prepare targets only need to be run once for your project. They give you a chance to tweak the launcher configuration files. The create targets are run every time you want to make a release.

The plugin provides launchers for the following platforms:
  • Linux
  • Mac
  • Windows
  • Executable Jar
The plugin also provides two other convenient targets, prepare-all-launchers and create-all-launchers which invoke the prepare- and create- targets for the four platforms above.

We'll use these convenience targets to create launchers for all platforms:
griffon prepare-all-launchers

This will create several new files for you:
  • installer/jar - the executable JAR launcher directory
  • installer/jsmooth - the Windows launcher directory
  • installer/linux - the Linux launcher directory
  • installer/mac - the Mac launcher directory
  • installer/jar/MANIFEST.MF - customize the manifest of the executable JAR
  • installer/jsmooth/$appName-icon.png - customize the .exe icon
  • installer/jsmooth/$appName.jsmooth - customize the .exe options
  • installer/mac/$appName.icns - customize the Mac icon
Once you are done customizing the configuration files, invoke the
griffon create-all-launchers target to actually create the launchers. This will create all of the launchers in the installer/$platform/dist directories and create archives of the launchers:
  • installer/jar/dist/$appName-$version.jar - the executable jar
  • installer/linux/dist/bin/$appName - the Linux launcher shell script
  • installer/linux/dist/$appName-linux-$version.zip - a zip of the Linux launcher
  • installer/mac/dist/$appName-$version.dmg - a disk image of the Mac launcher [only created if on a Mac]
  • installer/mac/dist/$appName.app - the Mac application bundle
  • installer/jsmooth/dist/$appName-windows-$version.zip - a zip of the Windows launcher
  • installer/jsmooth/dist/$appName.exe - the Windows launcher
  • installer/windows/dist/$appName-windows-$version.zip - copied from the JSmooth directory
  • installer/windows/dist/$appName.exe - copied from the JSmooth directory
As you make changes to your code, simply invoke griffon create-all-launchers and your latest changes will be packaged up. If you increment the version number of your app, the created artifacts will be updated to match.

Hopefully the Installer plugin makes it easy to distribute your Griffon applications to users on various platforms. In a future blog post, I'll cover creating installers for when a simple zip file is not enough.

Wednesday, March 12, 2008

Simile Timeline and Grails

I just put the finishing touches on a Grails plugin for Simile Timeline. There's already the RichUI Grails plugin which provides a nice wrapper for incorporating Timeline into your Grails app. However, I couldn't use RichUI because it links to the libraries out at the Simile site instead of serving them up locally. This causes cross-domain issues when using my depth extension.

Developing the plugin wasn't too terribly difficult and it was a great opportunity to dig into the internals of Grails a bit. Its plugin architecture is pretty slick. I actually created my plugin directly inside the plugins directory of a webapp I was working on. This allowed me to develop on both the webapp and the plugin at the same time. It's a pretty nice way to work when you have a pretty decent understanding of which pieces you want to go in your webapp and which you want to go in your plugin.

My Simile Grails plugin does a bit more than just packaging the Timeline javascript for serving locally. I put together a handy SimileTagLib for including the libraries so you can simply throw a
<simile:ajax/>
and a
<simile:timeline/>
tag in your view and that will include the javascript libraries. I ran into a bit of a stumbling block when trying to do this because the Javascript code is actually in the plugin. Hardcoding the links wouldn't work because I would have to change the link every time I updated the plugin's version number (Grails installs a plugin as {plugin.name}-{plugin.version}). Fortunately, you can look up the plugin's name and version at runtime:

def p = PluginManagerHolder?.pluginManager.getGrailsPlugin('simile')
return "<script type="text/javascript" src="${request?.contextPath}/plugins/${p.name}-${p.version}/js/${lib}.js?"></script>"

You may be wondering why I called it the Simile plugin instead of the Timeline plugin or what have you. I actually made the plugin generic enough that if I have a need to later, I can package all of the Simile code in the same plugin. The Timeline code already relies on the Simile Ajax library, so the plugin actually packages both libraries.

Along with the taglib, I also created some Timeline-related domain classes for working with timelines and events. I used these domain classes to bootstrap the example webapp I was working on. I simply did a:

grails create-app timelines
cd timelines
grails install-plugin simile
grails generate-all Timeline (domain class provided by the plugin)
grails generate-all TimelineEvent (ditto)
grails run-app

and I was up and running with a webapp that could create and edit timelines and events. Actually displaying the timelines took a bit more customizing, but not all that much.

I've made the source code for the completed webapp available via a Mercurial repository. The webapp gives you a CRUD style interface for creating timelines and timeline events. It supports both date-type of timelines as you see in the examples out at the Timeline site, as well as depth-type of timelines with my depth extension. I've put up a live demo on my development server at work. Feel free to play around with it by creating your own timeline and adding events. It'll stay up until as long as it isn't abused.