5. 5CONFIDENTIAL 5CONFIDENTIAL
What Is A Plugin?
A set of software components that
adds specific abilities to a larger
software application
https://secure.wikimedia.org/wikipedia/en/wiki/Plug-in_(
computing)
â
6. 6CONFIDENTIAL 6CONFIDENTIAL
What Is A Plugin?
ď§ Very similar to a Grails application project
ď§ Plus a *GrailsPlugin.groovy plugin descriptor file
ď§ Use grails createÂplugin to create one
ď§ Often adds artifacts, resources, scripts
ď§ Good for code reuse
ď§ Modular development
9. 9CONFIDENTIAL 9CONFIDENTIAL
Extension Points
ď§ The Build System
ď§ Spring Application Context
ď§ Dynamic method registration
ď§ Auto Reloading
ď§ Container Config (web.xml)
ď§ Adding new Artefact Types
10. 10CONFIDENTIAL 10CONFIDENTIAL
Types of Plugin
ď§ New functionality
⢠Datasources, UI Performance, etc.
ď§ Wrapper
⢠Spring Security, Searchable, Quartz, etc.
⢠Often have many scripts, e.g. Cloud Foundry, Database
Migration
ď§ UI
ď§ Bug fix
⢠http://grails.org/plugin/error-pages-fix, http://grails.org/plugin/aop-reloading-fix
ď§ Resource-only
⢠famfamfam
12. 12CONFIDENTIAL 12CONFIDENTIAL
What Can A Plugin Do?
ď§ Enhance classes at runtime
⢠Add methods, constructors, properties, etc.
ď§ Perform runtime Spring configuration
ď§ Modify web.xml on the fly
ď§ Add new controllers, tag libraries, etc.
13. 13CONFIDENTIAL 13CONFIDENTIAL
A Basic Plugin
class LoggingGrailsPlugin {
def version = "0.1â
def grailsVersion = "2.0 > *"
def doWithDynamicMethods = {
for (c in application.allClasses) {
c.metaClass.getLog = {->
LogFactory.getLog(c)
}
}
}
}
14. 14CONFIDENTIAL 14CONFIDENTIAL
Application Object
ď§ 'GrailsApplication' object
⢠available with the application variable in the plugin descriptor, and
the grailsApplication Spring bean
⢠holds convention information
⢠Convenient access to the config: grailsApplication.config
(since the holders are deprecated)
15. 15CONFIDENTIAL 15CONFIDENTIAL
Application Object
ď§ Useful methods like:
⢠get*Classes
⢠Retrieve all GrailsClass instances for particular artifact, e.g.
getControllerClasses()
⢠add*Class(Class clazz)
⢠Adds new GrailsClass for specified class,
e.g. addControllerClass(myClass)
⢠get*Class(String name)
⢠Retrieves GrailsClass for given name,
e.g. getControllerClass(name)
16. 16CONFIDENTIAL 16CONFIDENTIAL
Grails Artifacts
ď§ Some resource that fulfills a convention
⢠controllers, services, etc.
ď§ Represented by the GrailsClass interface
⢠http://grails.org/doc/latest/api/org/codehaus/groovy/grails/
commons/package-summary.html for API of all artifacts
ď§ Add new artifacts via the Artifact API
⢠http://grails.org/Developer+-+Artefact+API
17. 17CONFIDENTIAL 17CONFIDENTIAL
Grails Artifacts
ď§ Design decision: include or generate artifacts?
⢠Include is more automatic, no explicit step
⢠Including requires workflow to override
⢠Generate requires extra step but is more customizable
⢠Generation is static â old files can get out of date
ď§ Overriding domain classes?
⢠Really only an issue with create-drop and update, not with migrations
ď§ Overriding controllers?
⢠âun-mapâ with UrlMappings
"/admin/console/$action?/$id?"(controller: "console")
"/console/$action?/$id?"(controller: "errors", action: "urlMapping")
18. 18CONFIDENTIAL 18CONFIDENTIAL
Plugin Descriptor
ď§ Metadata
⢠version, grailsVersion range, pluginExcludes, dependsOn, etc.
ď§ Lifecycle Closures
doWithWebDescriptor
â Modify XML generated for web.xml at
runtime
doWithSpring â Participate in Spring configuration
doWithApplicationContext
â Post ApplicationContext initialization
activities
doWithDynamicMethods â Add methods to MetaClasses
onChange â Participate in reload events
19. 19CONFIDENTIAL 19CONFIDENTIAL
Configuring Spring
class JcrGrailsPlugin {
def version = 0.1
def dependsOn = [core:0.4]
def doWithSpring = {
jcrRepository(RepositoryFactoryBean) {
configuration = "classpath:repository.xml"
homeDir = "/repo"
}
}
}
Bean name is method name,
first argument is bean class
Set properties on the bean
21. 21CONFIDENTIAL 21CONFIDENTIAL
Planning for Customization
ď§ Plugins are compiled first, then the app; this means that
everything can be overridden
ď§ Prefer Spring beans to using new
ď§ Prefer dynamic instantiation (to allow class name override) to
using new
ď§ Use protected, not private and avoid static
ď§ Move logic from plugin descriptor to classes
22. 22CONFIDENTIAL 22CONFIDENTIAL
Overriding Spring Beans
ď§ Use loadAfter to define plugin load order; your beans
override other plugins' and Grails'
ď§ resources.groovy loads last, so applications can redefine
beans there
import com.mycompany.myapp.MyUserDetailsService
beans = {
userDetailsService(MyUserDetailsService) {
grailsApplication = ref('grailsApplication')
foo = 'bar'
}
}
24. 24CONFIDENTIAL 24CONFIDENTIAL
Plugging In Dynamic Methods
def doWithDynamicMethods = { ctx ->
application
.allClasses
.findAll { it.name.endsWith("Codec") }
.each {clz ->
Object
.metaClass
."encodeAs${clz.name-'Codec'}" = {
clz.newInstance().encode(delegate)
}
}
}
}
Taken from Grails core, this
plugin finds all classes ending
with âCodecâ and dynamically
creates âencodeAsFooâ methods!
The âdelegateâ variable
is equivalent to âthisâ in
regular methods
25. 25CONFIDENTIAL 25CONFIDENTIAL
Example Reloading Plugin
class I18nGrailsPlugin {
def version = "0.4.2"
def watchedResources =
"file:../grails-app/i18n/*.properties"
def onChange = { event ->
def messageSource =
event.ctx.getBean("messageSource")
messageSource?.clearCache()
}
}
Defines set of files to watch
using Spring Resource pattern
When one changes, event
is fired and plugin responds
by clearing message cache
26. 26CONFIDENTIAL 26CONFIDENTIAL
The Event Object
ď§ event.source
⢠Source of the change â either a Spring Resource or a
java.lang.Class if the class was reloaded
ď§ event.ctx
⢠the Spring ApplicationContext
ď§ event.application
⢠the GrailsApplication
ď§ event.manager
⢠the GrailsPluginManager
32. 32CONFIDENTIAL 32CONFIDENTIAL
Troubleshooting Dependency Management
ď§ Run grails dependencyÂreport
⢠See http://burtbeckwith.com/blog/?p=624 for
visualization tips
ď§ Increase BuildConfig.groovy logging level
⢠log 'warn' â 'info', 'verbose', or 'debug'
33. 33CONFIDENTIAL 33CONFIDENTIAL
Scripts
ď§ Gant: http://gant.codehaus.org/
ď§ Groovy + Ant - XML
ď§ Can be used in apps but more common in plugins
ď§ Put in the scripts directory
ď§ _Install.groovy
⢠runs when you run grails installÂplugin or
when it's transitively installed - don't overwrite!
might be reinstall or plugin upgrade
34. 34CONFIDENTIAL 34CONFIDENTIAL
Scripts
ď§ _Uninstall.groovy
⢠runs when you run grails uninstallÂplugin so
you can do cleanup, but don't delete
user-created/edited stuff
ď§ _Upgrade.groovy
⢠runs when you run grails upgrade (not when
you upgrade a plugin)
35. 35CONFIDENTIAL 35CONFIDENTIAL
Scripts
ď§ _Events.groovy
⢠Respond to
build events
ď§ Naming convention
⢠Prefix with '_' don't show inâ grails help
⢠Suffix with '_' 'global' script, i.e. doesn't needâ
to run in a project dir (e.g. createÂapp)
eventCreateWarStart = { name, stagingDir â
âŚ
}
eventGenerateWebXmlEnd = {
âŚ
}
36. 36CONFIDENTIAL 36CONFIDENTIAL
Scripts
ď§ Reuse existing scripts with includeTargets
⢠includeTargets << grailsScript("_GrailsWar")
ď§ Include common code in _*Common.groovy
⢠includeTargets << new File(              Â
  cloudFoundryPluginDir,                 Â
  "scripts/CfCommon.groovy")
37. 37CONFIDENTIAL 37CONFIDENTIAL
Testing Scripts
ď§ Put tests in test/cli
ď§ Extend grails.test.AbstractCliTestCase
ď§ stdout and stderr will be in target/cli-output
ď§ http://www.cacoethes.co.uk/blog/groovyandgrails
/testing-your-grails-scripts
ď§ Run with the rest with grails testÂapp or alone
with grails testÂapp ÂÂother
42. 42CONFIDENTIAL 42CONFIDENTIAL
Testing
ď§ Write regular unit and integration tests in test/unit
and test/integration
ď§ 'Install' using inline mechanism in
BuildConfig.groovy
⢠grails.plugin.location.'myÂplugin' = '../myÂplugin'
ď§ Test scripts as described earlier
ď§ Create test apps programmatically â all of the
Spring Security plugins do this with bash scripts
43. 43CONFIDENTIAL 43CONFIDENTIAL
Testing
ď§ Use build.xml or Gradle to create single target that
tests and builds plugin
ď§ Use inline plugin or install from zip
ď§ Zip install is deprecated, so better to use the
maven-install script
⢠Will resolve as a BuildConfig dependency (using
mavenLocal())
<target name='package' description='Package the plugin'
depends='test, doPackage, post-package-cleanup'/>
grails install-plugin /path/to/plugin/grails-myplugin-0.1.zip
44. 44CONFIDENTIAL 44CONFIDENTIAL
Testing
ď§ Use CI, e.g. CloudBees BuildHive:
⢠https://fbflex.wordpress.com/2012/07/12/using-cl
oudbees-buildhive-to-test-grails-plugins/
⢠https://buildhive.cloudbees.com/job/burtbeckwith
/job/grails-database-session/
46. 46CONFIDENTIAL 46CONFIDENTIAL
Becoming A Plugin Developer
ď§ Create an account at grails.org
ď§ Discuss your idea on the User mailing list
⢠http://grails.org/Mailing+lists
ď§ Submit a request at http://grails.org/plugins/submitPlugin
⢠Monitor the submission for questions and comments
ď§ Build (and test!) your plugin and run:
⢠grails publish-plugin --stacktrace
ď§ Profit!
47. 47CONFIDENTIAL 47CONFIDENTIAL
Source Control and Release Management
ď§ Internal server for jars & plugins is easy with Artifactory or
Nexus
repositories {
grailsPlugins()
grailsHome()
mavenRepo "http://yourserver:8081/artifactory/libs-releases-local/"
mavenRepo "http://yourserver:8081/artifactory/plugins-releases-local/"
grailsCentral()
}
48. 48CONFIDENTIAL 48CONFIDENTIAL
Source Control and Release Management
ď§ Release with grails publishÂplugin ÂÂrepository=repo_name
grails.project.dependency.distribution = {
remoteRepository(
id: "repo_name",
url: "http://yourserver:8081/artifactory/plugins-snapshots-local/") {
authentication username: "admin", password: "password"
}
}
49. 49CONFIDENTIAL 49CONFIDENTIAL
Source Control and Release Management
ď§ Use âreleaseâ plugin http://grails.org/plugin/release
⢠Sends tweet from @grailsplugins and email to plugin forum
http://grails-plugins.847840.n3.nabble.com/
⢠Will not check code into SVN for you
plugins {
build ':release:2.2.1', ':rest-client-builder:1.0.3', {
export = false
}
}
51. 51CONFIDENTIAL 51CONFIDENTIAL
Best Practices and Tips
ď§ Delete anything auto-generated that you don't use
⢠grails-app/views/error.gsp
⢠Empty grails-app/i18n/messages.properties
⢠_Uninstall.groovy and other generated scripts
⢠web-app files
⢠grails-app/conf/UrlMappings.groovy
⢠Empty descriptor callbacks
52. 52CONFIDENTIAL 52CONFIDENTIAL
Best Practices and Tips
ď§ Exclude files used in development or testing
⢠Use def pluginExcludes = [...]
⢠src/docs
⢠Test artifacts
ď§ Create .gitignore (manually or with
integrate-with âgit)
ď§ Open every file, decide that you own it or not
53. 53CONFIDENTIAL 53CONFIDENTIAL
Best Practices and Tips
ď§ Use dependency management in
BuildConfig.groovy, not lib dir
ď§ Change the minimum Grails version to 2.0 or the lowest
version you're comfortable supporting:
⢠def grailsVersion = '2.0 > *'
54. 54CONFIDENTIAL 54CONFIDENTIAL
Best Practices and Tips
ď§ Write tests
ď§ Run them often
ď§ Run tests before commit and especially before
release (use build.xml or Gradle)
ď§ Create test apps (ideally programmatically)
ď§ Programmatically build inline plugin location:
def customerName = System.getProperty("customer.name")
grails.plugin.location."$customerName" = "customer-plugins/$customerName"
55. 55CONFIDENTIAL 55CONFIDENTIAL
Best Practices and Tips
ď§ Prefer Java to Groovy (or @CompileStatic) for
performance if applicable
ď§ Instead of using inline plugins, install into a test
app and use Meld or another diff tool to keep in
sync
ď§ Before you release, run package-plugin and open
the ZIP, look at what's there
56. 56CONFIDENTIAL 56CONFIDENTIAL
Best Practices and Tips
ď§ Use a sensible naming convention, e.g.
grails.plugin.<pluginname>
ď§ Most plugin metadata is not optional:
⢠Fix grailsVersion
⢠Update description and documentation
⢠Set value for license, issueManagement, scm (and
organization, developers if applicable)
57. 57CONFIDENTIAL 57CONFIDENTIAL
Best Practices and Tips
ď§ Get to 1.0
⢠Initial version 0.1 is just a suggestion
⢠People trust 1.0+ more
ď§ Start using source control early
⢠Easy to run git init locally and later githubâ
ď§ Support your plugin!
58. 58CONFIDENTIAL 58CONFIDENTIAL
Best Practices and Tips
ď§ http://grails.org/Creating+Plugins
ď§ http://grails.org/The+Plug-in+Developers+Guide
ď§ http://blog.springsource.com/2010/05/18/managing-plu
gins-with-grails-1-3/
ď§ http://grails.org/doc/latest/guide/plugins.html