Managing Complexity - Multiple Components and Release Schedules


Have you ever needed to simultaniously develop in several modules? Did those modules have their own trunk/tags/branches in subversion, their own release cycle, etc.? Did everything build with a single Maven command?


If the answer to the first two is yes, chances are that the anser to the third one is no. In this short blog-post I’ll demonstrate a simple remedy for this. As an added bonus, I’ll also show how one can easily set it up for new developers (i.e. with a single checkout command).


Introduction



First, let’s have a look at the code we’re working with. Our development takes place in a library, and the enterprise application (.ear) that uses it. The latter consists of an EJB component and a web application.


In subversion, this may look like this:

svnroot
+- Projects
+- Customer 1
| +- Common Stuff
| | +- trunk
| | | +- pom.xml (com.customer1:common-stuff)
| | | +- ...
| | +- tags
| | +- ...
| +- Cool Project
| +- trunk
| | +- pom.xml (com.customer1:cool-project)
| | +- Enterprise Beans
| | | +- pom.xml (com.customer1:cool-project-beans)
| | | +- ...
| | +- Web Application
| | +- pom.xml (com.customer1:cool-project-webapp)
| | +- ...
| +- tags
| +- ...
+- ...

Now, usually, one would only need to checkout one directory, like Projects/Customer 1/Common Stuff/trunk or Projects/Customer 1/Cool
Project/trunk
. Unfortunately, due to new developments you find yourself developing both in Common Stuff and in Cool Project. This means that any
build requires two Maven commands. Not trivial. One can easily imagine that with three modules or more, things can really get out of hand.



Solution



In order to get everything to build using a single Maven command, we need a single pom.xml that defines everything. To use it, we want a directory in which each module we need to build is a subdirectory. This is not strictly necessary, but much more convenient and also allows the single checkout bonus I’ll introduce next. The directory structure now looks like this:

Project Sandbox
+- pom.xml (com.customer1:builder)
+- Common Stuff
| +- pom.xml (com.customer1:common-stuff)
| +- ...
+- Cool Project
+- pom.xml (com.customer1:cool-project)
+- Enterprise Beans
| +- pom.xml (com.customer1:cool-project-beans)
| +- ...
+- Web Application
+- pom.xml (com.customer1:cool-project-webapp)
+- ...

The pom.xml for com.customer1:builder (the first file in the diagram above) is trivial once you get past the preamble of groupId, artifactId, etc. You only need to set the packaging to pom and add a modules section as shown below:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.customer1</groupId>
<artifactId>builder</artifactId>
<version>none</version>
<packaging>pom</packaging>
<modules>
<module>Common Stuff</module>
<module>Cool Project</module>
</modules>
</project>

The new pom.xml does only one thing: when you issue a Maven command, it executes it for all modules — as anything short of installing the artifact anywhere is basically a no-op for the pom). This ensures that the standard mvn clean verify does indeed build the application and runs all tests.


Our main objective has been accomplished. Now, let’s make it easy to get started by setting this up with a single checkout.



Bonus: a single checkout



Adding the single checkout is also easy to do. There is a convention to add a trunks directory to Subversion, like this:

svnroot
+- Projects
+- Customer 1
| +- ...
+- ...
+- trunks

In the trunks directory, you place the new pom.xml listed above, and you add a Subversion property svn:externals to the trunks directory. It’s a multi-line property, so use propedit instead of propset. This is the value:

"Common Stuff"   "^/Projects/Customer 1/Common Stuff/trunk"
"Cool Project" "^/Projects/Customer 1/Cool Project/trunk"

The value above references the paths from the repository root, and ensures that an svn checkout or svn update will automatically get the trunks of Common Stuff and Cool Project as well.


Mission accomplished!