Quantcast
Channel: Nicholas Hagen » Maven
Viewing all articles
Browse latest Browse all 4

Grails 2.1 and Maven Integration: Multi-Module Projects

$
0
0

In the previous two blog articles (simple projects) (plugins), we touched on setting up standard Grails applications and plugins through Maven. In this article, we are going to talk about multi-module projects and how to build plugins and applications together as a single Maven build.

The first step is to create the directory structure and the plugins and applications for the Grails project. Use the following commands to setup the structure:


#> mkdir sample
#> cd sample
#> grails create-plugin plugin-a
#> grails create-plugin plugin-b
#> grails create-app test

If we look at our directory structure, we now have three directories, two representing plugins and one representing the application that will make use of the plugins. To enable this as a Maven multi module project, run the following command to generate all of the required POMs.


grails create-multi-project-build com.mycompany.grails:sample:1.0.0-SNAPSHOT

This will now create a POM in the main directory and each of the sub-directories. The command will also properly process the plugins as plugin projects and the applications as application projects. The root POM will be the parent POM containing each directory as a module. Note that this script will not generate or overwrite a given app or plugin if a POM already exists. In those cases, you will need to make sure you set the parent properly.

Let’s start in the parent POM in the main directory. After opening this POM you will notice it is pretty simple. The groupId, artifactId and version are all set to the values passed on the command line. The packaging is set to pom which associates this project as a parent POM. The last portion of the POM is the list of modules that should be executed with this project. This should contain all of the children modules.

Note that you can also include standard Maven and Java projects that are in the same directory structure by simply adding them as a module as well. This main project will resultingly build all children projects, regardless of type (grails-plugin, grails-app, jar, etc) and automatically build in the proper order based on the dependency management.

Now let’s look at the individual POMs. Let’s start with one of the plugins, such as plugin-a. If you recall from my previous article, this closely resembles the standard POM from a Grails plugin. The only real difference is that this POM contains a parent section that references the parent POM in the main directory. The rest is the same. You should follow the instructions in that article to ensure the POM and *GrailsPlugin.groovy file are in sync in terms of versioning. One last thing to note is that for plugins, Grails sets the groupId to org.grails.plugins to follow its conventional practice for 3rd party plugins. For internal sites, you may want to change this to your own company such as com.mycompany.grails. Make sure that if you change the groupId in the plugin POM, you also change it in any dependencies of any other plugins or applications.

Similarly, if we look at the POM from the test application directory, it too is the same per my previous article. Again, the only difference is the inclusion of the parent section. You should follow the instructions in that article to ensure the POM and application.properties file are in sync in terms of versioning. While we are here, let’s also add the two plugins as dependencies. The dependencies may already be listed if Grails automatically added them to the POM. If you wanted to be even more interesting, though, you could have plugin-a be a dependency of plugin-b and then add just plugin-a as a dependency of the application. Maven will correctly determine the order and build accordingly.

<dependency>
    <groupId>org.grails.plugins</groupId>
    <artifactId>plugin-a</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <type>zip</type>
    <scope>compile</scope>
</dependency>
 
<dependency>
    <groupId>org.grails.plugins</groupId>
    <artifactId>plugin-b</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <type>zip</type>
    <scope>compile</scope>
</dependency>

At this point, you should be able to go back to the main directory and run mvn clean package. That should build both plugins first followed by the application. The application will install both plugins into it and then build itself. In the end you will wind up with two ZIP archives that are installed in the Maven repository and one WAR archive that is installed.

We could stop here, but that’d be too easy. Maven has much more powerful constructs to simplify maintenance and configuration of the POMs.

The first thing I would change is the versions. Within multi-module projects, if all versions are the same and match the parent POM, then you do not need to include the version. The same applies for the groupId (although remember that the groupId of plugins may be different by convention). This will, then, remove the duplication of version in each POM (once in the parent and once in the children). To update the version, simply use the Maven release plugin or the mvn versions:set -DnewVersion=1.0.1-SNAPSHOT command. These will both cycle through the parent POM and all child modules to update the versions accordingly. As an example, my application POM now looks like this:

<parent>
    <groupId>com.znet.grails</groupId>
    <artifactId>sample</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</parent>
 
<artifactId>test</artifactId>
<packaging>grails-app</packaging>

The next thing I noticed is that each POM includes the Grails version via the grails.version property. In theory, within a multi-module project setup, all modules would be built using the same Grails version. As a result, we can place this in our parent POM and automatically inherit in our children POMs. So, remove the properties block from each child POM and copy it into the parent POM.

Up next? Dependencies. Maven has a neat construct called dependencyManagement that is particularly useful within parent POMs. Dependencies within the dependencyManagement section are not actually dependencies of any given project. Instead they are mere templates. In this way, you can define the groupId, artifactId, version, type, and scope for any or all dependencies. Then, within the dependencies section of any other child POM, you only reference the groupId and version. If all three plugins all made use of the same dependency, you would only need to change the version once rather than three times. Let’s add a couple dependencies to the parent POM (note that this works because we just added the grails.version property definition to the parent POM). You may also choose to add all the dependencies in the application POM as needed.

<dependencyManagement>
    <dependency>
        <groupId>org.grails</groupId>
        <artifactId>grails-dependencies</artifactId>
        <version>${grails.version}</version>
        <type>pom</type>
    </dependency>
 
    <dependency>
        <groupId>org.grails</groupId>
        <artifactId>grails-plugin-testing</artifactId>
        <version>${grails.version}</version>
        <scope>test</scope>
    </dependency>
</dependencyManagement>

We can now update each plugin and the main application POMs to change these two core dependencies to just the following. Now, we have a single place of configuration and maintenance.

<dependencies>
    <dependency>
        <groupId>org.grails</groupId>
        <artifactId>grails-dependencies</artifactId>
        <type>pom</type>
    </dependency>
 
    <dependency>
        <groupId>org.grails</groupId>
        <artifactId>grails-plugin-testing</artifactId>
    </dependency>
 
    ...
 
</dependencies>

You will notice that we just updated those two dependencies in all three children. Being every Grails app and/or plugin will include those dependencies, we can just add them to the dependencies (NOT dependencyManagement) block of the parent POM and remove from the children POMs. In this manner, all children projects that inherit from the parent will get those dependencies. Once again, we have a single place of maintenance. The Grails application child POM will still include several other dependencies, but those dependencies are not shared by ALL children dependencies. Only the dependencies that exist in all children should be placed in the parent POM dependencies block. One other potential issue is that if your multi module project also includes non-Grails projects such as Java/JAR projects, then this will also not work. One solution for that is to have a top level multi module project for all projects, including Java, and a single Grails project which itself is a multi-module project. For example:

sample/
    java-library-jar/
    grails-modules/
        plugin-a/
        plugin-b
        test-app/

Any of the Grails app can still reference the Java library but the grails-module will also be a multi-module project that will only contain Grails modules, thus allowing us the conventions in this article.

The next piece in comparing the POMs to simplify is the plugin section. Once again, the same configuration has been included in every POM. Rather than include in every POM, we can remove from each child POM and include in the main POM instead. Again, take note that if non-Grails projects exist, this will not work (consider using pluginManagement instead). Another workaround there other than the one above is to use Maven profiles and use a file existence activator based on the presence of the grails-app directory. In other words, if a grails-app directory exists in a given module, then activate it as a Grails build and invoke a set of plugins. Non-Grails projects will not have those plugins activated then.

If you remember from the previous articles we had to include some modifications to the Grails plugins, add custom plugins for doing search and replace for versioning artifacts, and update the Maven release process for it. As we now have a single location for our plugin management, we can make those changes to the parent POM rather than each individual POM. This dramatically improves maintainability.

One issue to be aware of is with the search and replace plugin. That plugin had a hard-coded file name. To avoid that issue, use the filesToInclude property instead to use GLOB-style matches.

<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>
        <filesToInclude>*GrailsPlugin.groovy</filesToInclude>
        <replacements>
            <replacement>
                <token>/\*\s*@@VERSION@@\s*\*/\s*".*"\s*/\*\s*@@VERSION@@\s*\*/</token>
                <value>/* @@VERSION@@ */ "${project.version}" /* @@VERSION@@ */</value>
            </replacement>
        </replacements>
    </configuration>
</plugin>

This will only match those files in the main directories ending with GrailsPlugin.groovy, which, by convention, all Grails plugins do.

Another issue to be aware of is that for the plugins, we changed the validate-plugin goal to run in a different phase so that the search and replace would work properly. If you keep this configuration in the parent POM, it will attempt to run the validate-plugin task on every project, which will fail in the application project as that is a Grails application, not a plugin. This unfortunately is difficult to solve. The best way to solve, without having to add duplicated plugin configuration to the POMs, is through Maven profiles. Maven profiles can have custom builds and plugins. Unfortunately, because plugins and apps look so similar, it is not easy out of the box to configure this. The best way I found is to use the existence of a file. For example, you could drop a grails.plugin file in each plugin directory and key off of that.

<profiles>
    <profile>
        <id>grails-plugin</id>
        <activation>
            <file>
                <exists>grails.plugin</exists>
            </file>
        </activation>
        <build>
            <plugins>
                 <plugin>
                    <groupId>org.grails</groupId>
                    <artifactId>grails-maven-plugin</artifactId>
                    <version>${grails.version}</version>
                    <configuration>
                        <fork>true</fork>
                    </configuration>
                    <extensions>true</extensions>
                    <executions>
                        <execution>
                            <id>default-validate-plugin</id>
                            <phase>initialize</phase>
                            <goals>
                                <goal>validate-plugin</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

This is not the cleanest approach and requires having an extra file in each plugin directory, but it works properly. The good news is that the configuration and maintenance is only in one place. You could even pull this configuration out to a super POM that all Grails projects (single module or multi-module) depend on.

If we now look at our child POMs, the plugins are almost empty, with the only piece being the repository and tools profile. As those are once again shared by the plugins and applications, pull this out into the parent POM. With that in place, the plugins and applications are now very simple only providing their metadata and dependencies. The Grails configuration and build process is all centralized to the parent or super POM.

For a complete example of this setup that includes the search and replace plugin and Maven release plugin support for local checkins, see my examples on GitHub.

Hopefully all three of these articles will help you to configure your Grails projects as a Maven build.

Other Articles in this Series:

  1. Grails 2.1 and Maven Integration: Simple Project
  2. Grails 2.1 and Maven Integration: Plugins
  3. Grails 2.1 and Maven Integration: Multi-Module Projects

Viewing all articles
Browse latest Browse all 4

Latest Images

Trending Articles





Latest Images