Converting Grails Applications to Plugins and vice versa
I was in London last week on vacation with my family and was lucky that there was a London GGUG meetup during my visit. David Dawson discussed modularizing Grails applications by refactoring them into multiple plugins (you can see the video of his talk here
). One thing that he mentioned was the idea of plugins that can be run like standalone applications since the structures of Grails applications and plugins are so similar, and I thought it would be interesting to look at the process of converting an application to a plugin or a plugin to an application.
The approaches I’ll discuss here include a lot of bias on my part about how I like to create apps and plugins, so YMMV. I also won’t go into partial conversions since David covered this well in his talk.
So to convert an application to a plugin, the general workflow would be something like
- Create the plugin descriptor, FooGrailsPlugin.groovy. The easiest way to do this is to run
grails create-plugin pluginname
and copy the generated file from there - delete everything from
application.properties
except theapp.grails.version
property - if you have jars in the lib directory that are available in a Maven repo, delete them and replace with
BuildConfig.groovy
dependencies - change any plugin and jar dependencies that are needed for development and testing but not when the plugin is installed to not be exported, by adding
export = false
- If you need the
_Install.groovy
,_Uninstall.groovy
, or_Upgrade.groovy
scripts (you probably don’t) grab those from the dummy plugin from step 1 (but delete any you don’t need, they’re all optional) - delete
ApplicationResources.groovy
if you aren’t using it and don’t depend on resources plugin - move code from
BootStrap.groovy
init()
toFooGrailsPlugin.doWithApplicationContext
and/orFooGrailsPlugin.doWithDynamicMethods
anddestroy()
to FooGrailsPlugin.onShutdown, and deleteBootStrap.groovy
- add a dependency for the
release
plugin inBuildConfig.groovy
- delete everything but the log4j configuration from
Config.groovy
- delete
UrlMappings.groovy
unless you have exported mappings; only keep the added ones - move bean definitions from
resources.groovy
toFooGrailsPlugin.doWithSpring
and deleteresources.groovy
- delete
grails-app/i18n
message bundle files unless you added messages; only keep the added ones - delete everything from
grails-app/views
that you don’t use (in particularerror.gsp
,index.gsp
, andlayouts/main.gsp
) - delete everything from
web-app
that you don’t use (including WEB-INF xml and tld files) - now would be a great time to write those tests you’ve been meaning to get to
- create one or more test applications to install the plugin into to ensure that it works as a plugin; consider scripting this
- write documentation for how to use the plugin; at a minimum a README file, but Grails gdoc files
would be much better (run
grails doc --init
to get started)
Converting a plugin to an application is similar, except for the most part reversed:
- Create a dummy application with
grails create-app appname
to copy missing files from- Move
BootStrap.groovy
,Config.groovy
,UrlMappings.groovy
, andApplicationResources.groovy
intograils-app/conf
, merging if needed - Move
resources.groovy
intograils-app/conf/spring
- Move message bundle files to
grails-app/i18n
, merging if needed - Move missing GSPs to
grails-app/views
- Move static resources from
web-app
- Move xml and tld files from
web-app/WEB-INF
- Move
- Move code from
FooGrailsPlugin.doWithApplicationContext
andFooGrailsPlugin.doWithDynamicMethods
toBootStrap.groovy
init()
, and code fromFooGrailsPlugin.onShutdown
todestroy()
- Move bean definitions from
FooGrailsPlugin.doWithSpring
toresources.groovy
- Delete the plugin descriptor
- Restore missing properties in
application.properties
- Delete the
_Install.groovy
,_Uninstall.groovy
, and_Upgrade.groovy
scripts - Remove the dependency for the
release
plugin fromBuildConfig.groovy
- Now would be a great time to write those tests you’ve been meaning to get to
- If you make changes in the plugin’s
doWithWebDescriptor
, rungrails install-templates
and add them tosrc/templates/war/web.xml
- If you add custom artifacts, or were supporting development-environment code or config changes in
onChange
and/oronConfigChange
, these aren’t directly doable in an application. Use the pluginatorplugin for these, and also for dynamic
web.xml
changes that require runtime logic
I’ve probably skipped a few steps and definitely omitted a lot of detail, but this should cover most of the process for each type of conversion.
Am I wrong if I say the BootStrap class can also be renamed to something like FooGrailsBootStrap instead of moving its content to the FooGrailsPlugin.groovy file as you suggest?
I am also interested in this because this is a much more elegant/intuitive solution than transforming BootStrap.groovy.
Burt: any comment on this? Does Grails 2.3.x still support using FooGrailsBootStrap.groovy style bootrstrapping in the plugins?
It should still work – if it doesn’t, create a JIRA.
Looks like application.properties also (still?) requires app.name.
Deleting that property leads to a rather opaque message:
java.lang.IllegalArgumentException: Property source name must contain at least one character
Which, when further explored, is found when constructing an instance of org.springframework.core.env.PropertySource via org.codehaus.groovy.grails.commons.env.GrailsEnvironment.GrailsConfigPropertySource.
It looks like plugin conversation may be easier.
For example, why do we need to move beans from resources.groovy
to FooGrailsPlugin.doWithSpring?
We can load beans form resources.groovy as usual.
Same for other files.
resources.groovy isn’t included in the zip by default – it’s typically only used for local development/testing. You get get it included, but it’s nonstandard since there’s a well-defined place in the descriptor for configuring Spring beans.
I followed this blog to create a private plugin which will include functionality of spring security core and rest, so we don’t have to configure this in each and every app. We just include the plugins jar and spring security is available for us in main application. Is this something we can do?. If yes, what should be the ideal approach to achieve this. More details on http://stackoverflow.com/questions/28291960/modularizing-spring-security-core-and-rest-in-a-private-plugin-so-that-it-can-be