Hands-on Maven 2: Working with multiple project builds
Building and testing simple projects using Maven 2 is straightforward. This section examines a second example demonstrating the more realistic and common case of a multiple-modules project.
The NumOps example is extended in this second example. A new SubOps class is added to support subtraction, and a new MulOps class is added to support multiplication.
However, the Operation interface and the AddOps class are now removed from the NumOps project. Instead, they are placed together with the new SubOps and MulOps classes in a new project called OpsImp. Figure 7 shows this relationship between the NumOps and OpsImp projects:
Figure 7. Relationship between NumOps and OpsImp
Dependencies among subprojects and submodules within a larger project is a frequently occurring scenario in software development. You can apply the technique shown here to any multimodule Maven project with interdependencies.
SubOps, shown in Listing 13, is coded similarly to AddOps. MulOps, not shown here, is similar; you can take a look at the code distribution for details (see Download).
Listing 13. The new SubOps class implementing the Operation interface
package com.ibm.devworks;
public class SubOps implements Operation {
public int op(int a, int b) {
return a-b;
}
public String getDesc() {
return "minus";
}
} |
The constructor of NumOps has now been modified to create an instance of SubOps and an instance of MulOps. See the source code distribution for details.
To work with these two projects, a master project has been created one directory above the NumOps and the OpsImp project directories. Both the NumOps and OpsImp projects use the standard Maven project directory layout. At the top level, the project directory consists of only a pom.xml file. Figure 8 shows the new sub-directory structure, immediately under the master directory:
Figure 8. Directory structure for a multimodule project
You can find the code for this multimodule project in the example2 subdirectory of the code distribution (see Download). The top-level pom.xml file is shown in Listing 14:
Listing 14. The top level pom.xml for the multimodule project
<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.ibm.devworks</groupId>
<artifactId>mavenex2</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Example 2</name>
<url>http://maven.apache.org</url>
<modules>
<module>NumOps</module>
<module>OpsImp</module>
</modules>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.ibm.devworks</groupId>
<artifactId>OpsImp</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
|
The new code is highlighted in bold. First, the artifact ID of this master project is mavenex2, and its packaging type is pom. This signals to Maven 2 that this is a multimodule project.
The <modules> tag then specifies the two modules that this project comprises: NumOps and OpsImp.
The submodules of this master project can inherit properties from this pom.xml file. More specifically, none of the submodules needs to declare JUnit as a dependency, even though they both contain unit tests. This is because they inherit the JUnit dependency defined at this top level.
The <dependencyManagement> tag does not specify dependencies that this module depends on. Instead, it is used mainly by submodules. Submodules can specify a dependency on any of the entries within the <dependencyManagement> tag without specifying a specific version number. This is useful for minimizing the number of edits required when a tree of projects changes dependency version numbers. In this case, the OpsImp project's version number is specified using ${project.version} This is a parameter that will be filled with the appropriate value during Maven execution.
Descending one level to the OpsImp directory, the pom.xml file for this module is shown in Listing 15:
Listing 15. The pom.xml file for the new OpsImp project
<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">
<parent>
<groupId>com.ibm.devworks</groupId>
<artifactId>mavenex2</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>OpsImp</artifactId>
<packaging>jar</packaging>
</project>
|
The <parent> element specifies the master POM that this module inherits from. Inheriting from the parent module simplifies this pom.xml greatly. All that is necessary is to override the artifact ID and packaging. This module inherits the parent's dependency: the JUnit module.
The NumOps pom.xml also inherits from the parent and is also quite simple. This pom.xml is shown in Listing 16:
Listing 16. The pom.xml for the NumOps project showing POM inheritance
<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">
<parent>
<groupId>com.ibm.devworks</groupId>
<artifactId>mavenex2</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>NumOps</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.ibm.devworks</groupId>
<artifactId>OpsImp</artifactId>
</dependency>
</dependencies>
</project>
|
The interesting item in the NumOps POM is the specification of the OpsImp project as a dependency. Note that no version number is specified in this dependency. The preferred version number is already specified within the parent's <dependencyManagement> element.
At the top-level project, you can now issue the mvn compile command to compile both modules or mvn test to run the unit tests of both modules. You can also run mvn install to install the packaged modules to your local directory. This allows any modules that depend on it to resolve the dependency without requiring access to the source code.


