Wednesday, October 21, 2009

Session Notes - Grails Plugin System Part 2

Presenter: Graeme Rocher

Technical Plugins

  • typically a bit more complicated to implement
  • can mix and match technical and functional plugins
  • provide programming apis

Key Concepts

  • plugin lifecycle
  • GrailsApplication object
    • encapsulates the conventions in a project
  • Spring BeanBuilder
    • runtime spring configuration--need to understand the spring dsl
  • Metaprogramming

Plugin Lifecycle Hooks

  • enclosures that allow you to manipulate the grails runtime
  • def doWithWebDescriptor = { xml -> }
    • useful for creating plugin that manipulates web.xml
  • def doWithSpring = {}
    • can use full power of spring within a grails plugin
    • written in groovy so have full access to groovy in configuring spring
  • def doWithDynamicMethods = { applicationContext -> }
    • metaprogramming
    • e.g. could add a method that interacts with hibernate
    • since you have access to the applicationContext you have access to more or less everything
  • def doWithApplicationContext = { applicationContext -> }
    • post-processing


  • programmatic modification of generated web.xml
  • useful for integrating third-party servlets/filters
  • groovy markupbuilder style dsl
    • easy to append to existing nodes in xml
  • automates a lot of configuration you'd otherwise have to do by hand
  • doesn't happen during war deployment


  • programmatically modify Spring ApplicationContext
  • groovy dsl that generates spring xml


  • exercise your groovy metaprogramming skills
  • add new methods and properties to existing types
  • "delegate" is equivalent to "it" in metaprogramming
  • in java terms a lot of what you have to do is DI, etc. and your code becomes rather cluttered
    • e.g. in GORM there's no reference to the fact that you're using Hibernate in your domain classes
    • specific references are abstracted away

Additional Hooks

  • onChange
    • can re-add a method to a class when it changes
    • e.g. specify def observe = ['plugins'] to watch for changes
  • onConfigChange

Plugin Life-Cycle Illustrated

  • grails loads first
  • loadPlugins() called next -- pluginmanager created
  • following this, the plugin lifecycle points are called
  • all plugins, core and otherwise, are loaded at the same time
    • can specify with your own plugins if they should load explicitly before or after core plugins

Programming by Convention

  • every grails project has a set of conventions
  • configuration can be automated through conventions
  • grails supplies an API to inspect conventions via some key interfaces
    • GrailsApplication
    • GrailsClass

The GrailsApplication Interface

  • used for programming by convention
  • not an interface in the fixed Java term; dynamic object
  • inspect GrailsClass instances
  • extensible to new artefact types
  • available as a variable called application in plugins
  • interesting methods
    • Class[] getAllClasses() // gets all loaded classes
    • GrailsClass get*Class(String name) // gets a class by name
    • boolean is*Class(Class c) // checks if a class is of a given type
    • GrailsClass[] get*Classes() // all grailsclass instances of type
    • void add*Class(Class c)
    • all the * stuff in these examples are where you substitute the grails class type, e.g. getControllerClass("fully.qualified.class.Name")
  • can add new artefact handlers to add completely new artefact types to grails
    • can tell grails what the conventions are, e.g. "ends with X" or "lives in this directory"

Artefact Types

  • built in types
    • DomainClass
    • Controller
    • TagLib
    • Service
  • extensible to new by implementing ArtefactHandler

The GrailsClass Interface

  • allows you to further analyze conventions for each class
  • contains methods to get various forms of the class name
    • getShortName(): UserController (no package)
    • getPropertyName(): userController (bean name)
    • getNaturalName(): User Controller
    • others ...
      • getLogicalPropertyName(), getPackageName(), etc.

Demo: Building a Technical Plugin

  • building an API to twitter
  • using twitter4j -- twitter api for java
    • drop jar in lib directory of project
  • create a new public twitter bean in the doWithSpring plugin point
    • default constructor assumes public timeline
  • in doWithDynamicMethods are going to add ability for spring security users to submit to twitter
    • adding dynamic tweet() method to string class
    • String.metaclass.tweet = { ->
      def auth = SecurityContextHolder.context?.authentication
      if (auth instanceOf UsernamePasswordAuthenticationToken) {
      def twitter = new Twitter(auth.principal, auth.credentials)
      } else {
      throw new TwitterException("user not logged in")

Convention-Based Programming

  • have classes in the system, want to mark these as a "tweeter"
  • want to be able to call twitter methods to a user who's marked as a tweeter
  • implement this in doWithDynamicMethods
  • inside each hook there is an implicit application keyword that represents the application context
    • application.domainClasses.each { GrailsDomainClass dc ->
      def metaClass = dc.metaClass
      metaClass.tweet = { String message ->
      def twitter = new Twitter(delegate.username, delegate.password)

      metaClass.getLatestTweets = { ->
      def twitter = new Twitter(delegate.username, delegate.password)
      twitter.getPublicTimeline().text // property doesn't exist but gpath automatically grabs text property of each object


  • more advanced knowledge of the grails plugin system opens up a world of possibilities
  • combine technical and functional plugins to enable new levels of productivity
  • harness the power of groovy metaprogramming!
  • helps avoid reinvention and duplication across applications


  • inside any plugin you can do grails create-script and specify a gant script
    • used to extend the grails command line interface
  • plugins can provide different scaffolding templates to override the default ones and create your own uis
  • "plugins are the most important thing about grails"
  • any thoughts about how to eliminate bad/abandoned plugins from the central repository?
    • hudson build of all tests on all plugins done by a user
    • thoughts of integrating this into to give plugins a health status
  • plans to move plugins into git instead of SVN?
    • in grails 1.2 you can export plugin as a zip only so you can manage your source in git but still deploy to svn
    • complete move to git might be considered further down the line
  • SpringSource officially supports specific plugins

No comments: