Multi Module Dependency Management

Dependency inheritance (explained in previous chapter) is useful when a dependency is required by all child modules. When a dependency is required by some or even many, but not by all modules, then use dependency management feature.

The example code extended-multi, used in this tutorial, is available at GitHub Maven Examples. Download the examples as zip and extract it to some location or clone it with git.

In extended-multi, util module uses commons-lang3 as dependency and let’s assume that another module app also require commons-lang3. Now, both app/pom.xml and shared/util/pom.xml should declare this dependency. When multiple modules require a dependency then move it the top-level pom.xml as dependencyManagement.

To move commons-lang3 to dependency management, first modify the extended-multi/pom.xml and following lines

extended-multi/pom.xml

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.6</version>
        </dependency>
    </dependencies>
</dependencyManagement>

The dependency management declaration is dependencies declaration wrapped in dependencyManagement element.

Next, modify app/pom.xml and add the following to dependencies element. Notice that version is not specified.

app/pom.xml

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

Next, modify shared/util/pom.xml and remove version element from the commons-lang3 dependency. After modification the dependency is as follows

shared/util/pom.xml

<dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>

To use commons-lang3 from app module, modify the greet() method of App.java as follows

app/src/main/java/app/App.java

....
import org.apache.commons.lang3.StringUtils;

public class App {

    public String greet(String name) {
        StringUtils.trim("dummy");
        return Util.join("Hello ", name);
    }
....

Now, run mvn test and project should build successfully.

To summarize, we added the dependency in top level pom.xml as dependencyManagement and referred it in required modules using simpler reference i.e. just groupId + artifactId and without the version.

Dependency Management vs Inheritance

Dependency management

  • used when dependency is required by some of the child modules
  • declared in parent POM using dependencyManagement/dependencies/dependency element
  • need to refer them in the required child modules using simpler reference i.e. just groupId + artifactId, but without the version

Dependency inheritance, which we explained in the previous tutorial

  • used when dependency is required by all child modules
  • declared in parent POM using dependencies/dependency element
  • no need to refer them in the child modules as they are inherited by all child modules by default
 
 

Plugin Management

Let’s suppose that all modules of extended-app should compile as Java 1.7. One way to do this is configure compiler plugin by adding following snippet to app, util and config POM.

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
    </plugins>    
</build>

Instead, the better approach is to centralize the plugin configuration using pluginManagement element in top level POM. To do that, add following lines to the top level POM extended-multi/pom.xml

extended-multi/pom.xml

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

Next, we need to refer the plugin in the required modules. Modify all module POM, app/pom.xml, shared/util/pom.xml and shared/config.pom and add following lines

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
        </plugin>
    </plugins>
</build>

While referring the plugin in the modules, specify plugin groupId, artifactId and there is no need to repeat the version as well as the configuration as they are inherited from the parent POM.

 
 

Child module POM can override the configuration if required. For example, replace compiler plugin reference in shared/config/pom.xml as below

shared/config/pom.xml

<build>        
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>        
</build>

Run mvn clean test, app and util module are complied as Java 1.7 where as config module is complied as Java 1.8. We can investigate the compiled classes and know Java version with

$ javap -verbose app/target/test-classes/app/AppTest.class  | grep major
$ javap -verbose shared/util/target/test-classes/util/UtilTest.class  | grep major
$ javap -verbose shared/config/target/test-classes/config/ConfigServiceTest.class  | grep major  

## for Java 1.7, output is major version: 51
## for Java 1.8, output is major version: 52

For more complicated examples of Maven Multi Module Project, refer Official Maven Book - Maven By Example.

The next chapter, the concluding part of the tutorial, covers integration of Maven with Eclipse IDE.