3. Plug-in Structure + grails-app + controllers + domain + taglib etc. + lib + src + java + groovy + web-app + js + css Just like a normal grails application The only difference is the presence of a file *GrailsPlugin.groovy The content of the grails-app directory will not be copied into main source tree. Code in src and lib directory will be compiled into main app’s web-app/WEB-INF/classes
5. Overview of Plugin Closures A plugin can specify multiple closures each of which can manipulate Grails: doWithSpring – Participate in Spring configuration doWithApplicationContext – Post ApplicationContxtinitialisation activities doWithWebDescriptor – Modify the XML generated for web.xml at runtime doWithDynamicMethods – Add methods Some Examples
6. Hooking into Spring Configuration import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.context.support.ReloadableResourceBundleMessageSource; class I18nGrailsPlugin { def version = 0.1 def doWithSpring = { messageSource(ReloadableResourceBundleMessageSource) { basename = "WEB-INF/grails-app/i18n/messages" } localeChangeInterceptor(LocaleChangeInterceptor) { paramName = "lang" } localeResolver(CookieLocaleResolver) } } This plugin sets up the Grails messageSource bean and a couple of other beans to manage Locale resolution and switching. It using the Spring Bean Builder syntax to do so.
7. Hooking into web.xml Generation defdoWithWebDescriptor = { webXml -> defmappingElement = webXml.'servlet-mapping' deflastMapping = mappingElement[mappingElement.size()-1] lastMapping + { 'servlet-mapping' { 'servlet-name'("grails") 'url-pattern'("*.dispatch") } } } Here the plugin goes through gets a reference to the last <servlet-mapping> element and appends Grails' servlet to the end of it using XmlSlurper's ability to programmatically modify XML using closures and blocks.
8. Doing Post Initialization Configuration defdoWithApplicationContext = { appCtx-> SessionFactorysf = appCtx.getBean("sessionFactory") // do something here with session factory } Sometimes it is useful to be able do some runtime configuration after the Spring ApplicationContext has been built. In this case you can define a doWithApplicationContext closure property.
9. Adding Dynamic Methods defdoWithDynamicMethods = { applicationContext-> String.metaClass.swapCase= {-> defsb = new StringBuffer() delegate.each{ sb<< (Character.isUpperCase(it as char) ? Character.toLowerCase(it as char) : Character.toUpperCase(it as char)) } sb.toString() } assert "UpAndDown" == "uPaNDdOWN".swapCase() } In this example we add a new method swapCase to java.lang.String directly by accessing its metaClass.
10. Reload Events Grails applications must be reloadable during development Plug-ins can define watchedResources that fire onChange event when modified
11. Example Reloading Plug-in Class I18nGrailsPlugin{ defwatchedResources = “file:../grails-app/i18n/*.properties” defonChange = { event -> defmessageSource = event.ctx.getBean(“messageSource”) messageSource?.clearCache() } } When one changes,event is fired and plugin responds by clearing message cache
12. The Event Object event.source - The source of the event which is either the reloaded class or a Spring Resource event.ctx- The Spring ApplicationContext instance event.plugin - The plugin object that manages the resource (Usually this) event.application - The GrailsApplication instance
13. Plugin Load Order Plugin dependencies defdependsOn = [foo: “ * > 1.0”, bar : “ 1.0 > 1.2”] Load Order defloadAfter = [‘controllers’]
14. Installing & Distributing grails package-plugin Excluded artefacts when Distributing grails-app/conf/DataSource.groovy grails-app/conf/UrlMappings.groovy build.xml Everything within /web-app/WEB-INF grails install-plugin [name] [file path] [url] Specifying plugin location in ‘BuildConfig.groovy’ //useful for modular app where app & plugin in the same directory Grails.plugin.location.’grails-ui’ = “../grails-grails-ui”