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
doWithWebDescriptor
- 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
doWithSpring
- programmatically modify Spring ApplicationContext
- groovy dsl that generates spring xml
doWithDynamicMethods
- 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)
twitter.updateStatus(delegate)
} 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)
twitter.updateStatus(message)
} 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
}
}
- application.domainClasses.each { GrailsDomainClass dc ->
Summary
- 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
Q&A
- 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 grails.org 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