Microservices. Streaming data. Event Sourcing and CQRS. Concurrency, routing, self-healing, persistence, clustering...learn how Akka enables Java developers to do all this out of the box! Brought to you in partnership with Lightbend.

Maven 2 has fantastic multi-deliverable project build capabilities. We'll investigate this feature by Mavenizing Guice's other Jar deliverables. We'll Mavenize Guice's Spring bridge, Servlet injector, and Struts 2 plugin in this part of the tutorial, and Mavenize the Struts 2 example in Part 3.

If you're starting this tutorial with part 2, you'll need to go back to part 1 of the tutorial and download Guice's source, install cglib-nodep-2.2_beta-1, and put the pom.xml listed at the end of part 1 in the root of Guice's source directory. Assuming you've done all of that (or you've finished part 1), you're ready to proceed with part 2 of the tutorial.

A Quick Tour of Multi-Module Maven Builds

Maven 2 has the ability to build multiple related (sub)projects in one build cycle, making it easy for multiple teams to share the same build cycle and dependencies, and easily find out if the build passes or fails as a whole. Pom.xml files are actually easier to manage for larger builds because only dependencies need to be listed each POM file, but not version numbers. Version numbers for dependencies, as well as all other project information, are listed in what's often referred to as the "parent POM" in the <dependencyManagement> section, and do not have to be repeated in other POMs. This feature makes both adding new project modules and upgrading dependencies very easy. The project's parent pom and has a <packaging>pom</packaging> instead of <packaging>jar</packaging>, making it fairly easy to spot.

Although a parent pom file can be located anywhere in the project, it is usually located at the root of the project's source directory. Each sub-project, known as a module, is typically a sibling of the parent pom.xml file. Each of these directories is listed in a <modules> section of the parent POM so Maven 2 knows to look for it, and each module's POM file is frequently referred to as a "child POM".

The whole project build can be launched from the directory where the parent POM is located, building all modules listed in the parent pom's <modules> section. Alternatively, a single module may be built by running Maven in that module's directory. All project information is inherited from the parent pom, but in order for a dependency to be used in a module it must be listed in the <modules> section. If one project module is used as a dependency in another, the same dependencies do not need to be re-listed since they are transitive. Dependency scopes can be listed in either the parent or child POM, depending on if the scope is applicable project wide (such as JUnit) or for each module (such as Spring, which we'll see later)

You'll notice that the version numbers aren't listed in any of the dependencies. If you want to make a version of a jar "sticky" for a child POM, even though the version may be changed in the parent, you can declare the version number. However, the practice is not recommended. You'll also notice that we've added a <parent> section that indicates what POM should be used as a parent POM.

Now we need to update the POM from part 1 (in the guice-1.0-src directory) to make it into a parent POM.

Change <packaging>jar</packaging> to <packaging>pom</packaging> at the top

Change <artifactId>guice</artifactId> to <artifactId>guice-parent</artifactId> at the top

Add the <modules> section just below the <build> section (it won't work if you put it before the <build> section, though I'm not sure why)

<modules> <module>guice</module> </modules>

Put a <dependencyManagement> and a corresponding </dependencyManagement> element around the <dependencies> section. This tells Maven you'll be using these dependencies throughout your child POMs

Notice that the name of the module is the same name as the directory where the code for that module resides, which is how Maven locates project modules that are to be built as part of a build cycle. Child modules do not have to be listed, but can still take advantage of the parent POM's definitions. We'll see this type of situation in part 3.

Once you've added the child POM in the guice directory and updated the parent POM in the guice-1.0-src directory, run

>mvn compile

in the guice-1.0-src directory and you should see BUILD SUCCESSFUL

If you build the site, you'll find the static project documentation located in the guice-1.0-src/target/site directory and the report results in guice-1.0-src/guice/target/site directory. This is a known issue -- it would make sense that all module websites be accessible from the parent website -- but it's something that unfortunately has to be coped with until it's fixed.

The Spring Bridge Module

Add <module>spring</module> to the <modules> section of your parent POM

Remove the <scope>test</scope> from both the spring-core and spring-beans dependencies in the parent POM since they are now being used as dependencies of Guice artifacts

Add a version property - this will be helpful when we need to upgrade guice artifacts:

<properties> <guiceVersion>1.0</guiceVersion></properties>

Since the Spring Bridge module requires the core Guice module, we need to add it to the <dependencyManagement> section. Add the following XML the parent POM in the <dependencies> section:

If your build isn't compiling at this point, the <scope>test</scope> declaration may be present in your parent POM for the spring-core and spring-beans dependencies. Once you remove them, it should compile just fine.

Attempting to run the tests though, we find that the SpringIntegration test class isn't compiling, indicating that there is no import com.google.inject.PerformanceComparison package. Looking into it, Maven is treating the parent class of the TeeImpl static inner class as a package on the import statement. Doing some quick searches on Google, it looks like this isn't the first time this problem has been encountered, but it's not clear if it's been entered into JIRA yet. I found a similar Surefire issue SUREFIRE-44, but I don't think this is quite the same. Fortunately, the import is not used in the SpringIntegrationTest class where is is being imported, so for now we'll remove the import; in all reality, though, this could be real showstopper and almost was in our case. After removing the line import com.google.inject.PerformanceComparison.TeeImpl; from the SpringIntegrationTest.java class, the unit tests work fine and we're back on track.

The Servlet Module

Add <module>servlet</module> to the <modules> section of your parent POM

Add the following dependencies to your parent POM in the <dependencies>section:

Thankfully all of the unit tests pass without any trouble and we can confidently move onto the Guice Struts 2 Plugin.

The Guice Struts 2 Plugin

Add <module>struts2/plugin</module> to the <modules> section of the parent POM because the struts2 plugin is located in that directory relative to the guice-1.0-src directory

Add the following dependencies to the parent POM in the <dependencies> section. Notice that even though there are quite a number of plugins that are required to use Struts2 in the struts2/lib folder, we only need to specify the struts2-core dependency

The Guice team released version 1.0.1 of the Struts2 plugin after the initial release of Guice, and you may optionally update the GuiceObjectFactory source file in the module to reflect that change. The updated source for the GuiceObjectFactory can be found here. While not needed for the purposes of this tutorial series, I would recommend this change if you are building the plugin in your development team.

You've probably noticed how we added a <relativePath>../..</relativePath> element to the parent declaration. This doesn't need to be declared if the parent POM is in the parent directory directly above the module, but does if the parent POM is located elsewhere in the project. In our case, the parent POM is two directories up, but could have been in a completely different location.

We've also created a <resources> declaration in the <build> section. This allows the struts-plugin.xml file to be added to the jar when it is packaged. We've also added an <excludes> section that excludes all *.java files within the project to prevent them from being added. Even though we added a <build> section, we didn't have to re-declare the source version or build and test directories.

Putting It All Together

The Maven 2 commands can be executed from the root source directory (guice-1.0-src) to compile, test, and package, or the commands can be run in each of the project directories, such as guice-1.0-src/guice, guice-1.0-src/spring, etc. However, each of the modules a given module is dependent on must be installed in your local repository. For instance, if we want to work on the Struts 2 plugin code, the guice module and guice-servlet module must both be installed by running the command

>mvn install

If you run the command

>mvn package

from the guice-1.0-src directory, you'll find a Jar file in the target directory of each of the modules that is identical to the Jars in the guice-1.0-src/build/dist directory, file-for-file and almost byte-for-byte (okay -- except the core guice Jar, but that's because the dependencies aren't being repackaged into the Jar). All of the Jars can be posted to your favorite repository by running the

>mvn deploy

command. At that point, your teammates can now use the newly created Jars.

Conclusion

We've migrated the Guice project from generating a single Jar artifact to generating multiple Jar artifacts that can all be generated and shared with a single command. We've had to do a little bit of work along the way to get Maven to work with the Guice source, but the source code didn't undergo any major refactorings or restructuring.

In the third and final part of the tutorial, we'll look at how to use and take advantage of Maven's Jetty plugin.

Resources

The parent POM with all modules and dependencies declared up to this point can be found here.

The Glassfish team is using Maven 2's multimodule build capabilities to build Glassfish V3. Be sure to take a look at their source code repository.

Microservices. Streaming data. Event Sourcing and CQRS. Concurrency, routing, self-healing, persistence, clustering...learn how Akka enables Java developers to do all this out of the box! Brought to you in partnership with Lightbend.