My last blog article explained using the Maven integration in the recently released 2.1 version of Grails on simple applications. Today, we are going to look at using the Maven integration and build, manage, and deploy plugins. Plugins and applications in Grails are very similar to each other. The big difference is that a plugin has a special plugin descriptor file named *GrailsPlugin.groovy
where *
is the name of the plugin. Other than that, they behave very similar to each other. Plugins generally do not have as many dependencies, however, as they are not required to include the web application environment typically.
The first step is to create a plugin. Instead of using grails create-app
, we will use the plugin-variant grails create-plugin
. At that point, we can once again use the create-pom
command to create the resulting POM.
#> grails create-plugin my-plugin
#> cd my-plugin
#> grails create-pom com.mycompany.groupid
Before looking at the POM, we should update the plugin descriptor first. Grails automatically creates a plugin descriptor based on the name of the plugin. In this case, the descriptor is named MyPluginGrailsPlugin.groovy
. One of the first few lines is the defined version. In order to be more Maven-compliant by using SNAPSHOT
in the version while in development mode, change the version to 0.1-SNAPSHOT
.
Now, let’s crack open the POM. You will notice that the generated POM for the plugin closely resembles the generated POM for a Grails application. The only major difference is the number of required dependencies. Per my previous article, Grails automatically generates the dependencies based on the dependencies block in the BuildConfig.groovy
file.
The first step that is required is to change the packaging type. Grails applications use the packaging type of grails-app
; however, plugins need to use grails-plugin
. As a result, make sure to update the packaging type to grails-plugin
. This configures Maven for the proper plugins and commands when building and deploying the plugin. Whereas the Grails application creates WAR artifacts, the Grails plugins creates ZIP artifacts.
The next step is to change the version. The version in the POM must match precisely with what was configured in the plugin descriptor. In fact, the validation plugin in the Grails Maven plugin ensures they match before deployment. As a result, set the version to match what is in the plugin descriptor file (ie: 0.1-SNAPSHOT).
At this point, you may also choose to set the name, description, include other dependencies, etc. Now we are ready to build and deploy.
#> mvn clean install
This will build and install the plugin as a ZIP archive in your local Maven repository. You could also deploy it to a remote Maven repository by configuring the deployment repository.
With our plugin built and deployed locally, we can now include this plugin in our previous sample application. If you do not have a sample application, simply follow my previous article or run these commands:
#> cd ..
#> grails create-app myapp
#> cd myapp
#> grails create-pom com.mycompany.groupid
Within the application’s base directory, open its POM file so we can add our plugin as a dependency. Adding plugins as dependencies are just like any other dependency in Maven and include the GAV (groupId/artifactId/version) coordinates. The important piece to include is the type
. Plugins in Grails are deployed as zip files, so the type should be zip
.
<dependency> <groupId>com.mycompany.groupid</groupId> <artifactId>my-plugin</artifactId> <version>0.1-SNAPSHOT</version> <type>zip</type> </dependency> |
The groupId and version should obviously match what was deployed/installed for the plugin. You may also set the scope to compile or runtime depending on how the plugin is needed. Otherwise, at this point, you should be able to build the application again.
#> mvn clean package
If that runs successfully, you will notice that the my-plugin plugin is installed in the plugins subdirectory. This means the plugin was properly installed into the application. You now have a working Grails application, plugin included, built completely with Maven.
But not so fast….
If you recall, we had to ensure that the version number was the same in both the POM and the plugin descriptor. This becomes a problem if you are using the Maven release plugin. The Maven release plugin automatically configures the version and re-builds the project to release it accordingly. This will cause a validation error since the plugin descriptor will still be expressed as a SNAPSHOT
, but the POM will be a release build without SNAPSHOT
. Similar, when the release plugin advances to the next development version, the plugin descriptor will still be off.
The solution to this, or at least my solution unless others have better ones, is to use search and replace in Maven to automatically replace the versions in the plugin descriptor with those in the POM. There are a few different replacement options in Maven, but the one I’m using is the maven-replacer-plugin.
The best way I thought of doing search and replace was doing a prefix and a suffix as comments. This way you could easily match and replace with a simple regex as well as keep the file compilationly valid. For example, here is a portion of my plugin descriptor:
def version = /* @@VERSION@@ */ "0.1-SNAPSHOT" /* @@VERSION@@ */
We can now use our search and replace plugin in Maven to easily replace that. You may also want to consider doing this for other properties such as the title, description, SCM path, etc so that you can control them all from a single location in the POM. Then, using the search and replace plugin, you configure it in this manner:
<plugin> <groupId>com.google.code.maven-replacer-plugin</groupId> <artifactId>replacer</artifactId> <version>1.5.0</version> <executions> <execution> <phase>validate</phase> <goals> <goal>replace</goal> </goals> </execution> </executions> <configuration> <file>MyPluginGrailsPlugin.groovy</file> <replacements> <replacement> <token>/\*\s*@@VERSION@@\s*\*/\s*".*"\s*/\*\s*@@VERSION@@\s*\*/</token> <value>/* @@VERSION@@ */ "${project.version}" /* @@VERSION@@ */</value> </replacement> </replacements> </configuration> </plugin> |
You will notice that the phase it its bound to is validation. However, that is also the phase that the validation of the plugin for the Grails Maven plugin occurs in. Unfortunately, there is no way (that I’m aware of) to selectively order plugin execution when two goals are in the same phase. My solution is to run the replacement in validate and then change the validation plugin to run in initialize, which occurs right after validate. For example, change the Grails Maven configuration to the following.
<plugin> <groupId>org.grails</groupId> <artifactId>grails-maven-plugin</artifactId> <version>${grails.version}</version> <configuration> <fork>true</fork> </configuration> <extensions>true</extensions> <!-- RUN VALIDATION IN INITIALIZE PHASE SO REPLACEMENT PLUGIN RUNS PROPERLY IN VALIDATE PHASE --> <executions> <execution> <id>default-validate-plugin</id> <phase>initialize</phase> <goals> <goal>validate-plugin</goal> </goals> </execution> </executions> </plugin> |
You should now be able to change the version in the POM and run mvn clean package
and have it update the plugin descriptor and successfully build it. The last step, similar to my last article, is to perform an scm:checkin
during preparationGoals
and completionGoals
of the release plugin so that the plugin descriptor is committed to source along with the associated POM.
<plugin> <artifactId>maven-release-plugin</artifactId> <version>2.3.2</version> <configuration> <preparationGoals>clean verify scm:checkin -Dmessage="perform release"</preparationGoals> <completionGoals>validate scm:checkin -Dmessage="prepare for next release"</completionGoals> </configuration> </plugin> |
You need both preparation and completion goals so that you both commit the updates during the release and after the release and upon configuring the next version. Note that this may unfortunately lead to 4 checkins per release (2 for the release plugin, 2 from this configuration).
You can now build, package, release, and deploy the plugin purely using Maven. You will most likely want to include the above configuration in a parent or super-POM so that all Grails projects can inherit from it to reduce the configuration and management of each Grails project.
Being this is the first release of the pure Maven integration into Grails, I do have the following feature requests for future updates:
- The
create-pom
script should detect a plugin and set packaging to grails-plugin - The
create-pom
script should consider changing the version to 0.1-SNAPSHOT, but then it would differ from what is in the plugin descriptor by default, so this may not be a good idea - The
create-pom
script should set the description to an empty string rather than null or the version number
My next article will touch on the multi-module support in Maven and Grails and how to build the plugins and applications as a single build.
Other Articles in this Series:
- Grails 2.1 and Maven Integration: Simple Project
- Grails 2.1 and Maven Integration: Plugins
- Grails 2.1 and Maven Integration: Multi-Module Projects