Maven Multi Module

As project size and complexity increases, it makes sense to split the project into multiple modules. While it is always possible to split the project into multiple projects and link them as dependencies, it complicates the build process. In Maven, the preferred approach is to structure the project as multi module project and delegate the rest to Maven.

In this tutorial, we go through a simple multi module project. Download the example code from GitHub Maven Examples and extract it to some location before going further.  

Simple Multi Module Project

The Simple Multi Module project contains two modules - app and util. The util module provides a static method that joins multiple strings using Apache Commons Lang library and the app module calls it. Snippet from App.java is shown below

simple-multi/app/src/main/java/app/App.java

public class App {

    public static void main(String[] args) {
        System.out.println(new App().greet("World!"));
    }

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

To build the project, run

$ cd simple-multi
$ mvn clean test

Maven builds both the modules in proper order, runs tests and outputs nice little summary.

Maven Multi Module Project Build Summary

Structure of multi module project

The layout of Simple Multi is

Maven Multi Module Project Simple Multi Layout

The directory simple-multi is the top directory of the multi module project. It contains the parent POM also known as top-level POM, but the top level directory doesn’t contain any source folder.

Along with parent POM, the top level directory also contains two sub folders – app and util. These module folders are regular maven projects with source directories and pom.xml.

The top level as well as module POM differs slightly from the regular POM and let’s see what they holds.

 
 

Top Level POM

Maven Multi Module Project requires a parent POM at the project’s top level directory. The contents of the file is

simple-multi/pom.xml

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.codetab</groupId>
  <artifactId>simple-multi</artifactId>
  <version>1.0</version>
  <packaging>pom</packaging>

  <modules>
      <module>app</module>
      <module>util</module>
  </modules>

</project>

It defines project coordinates - groupId, artifactId and version - as in any normal project, but the packaging type is specified as pom instead of usual jar or war. Since top level project doesn’t contain any source directory, packaging is set as pom. The modules/module elements add the modules - app and util.

To sum up, the top level POM specifies packaging type as pom and lists all sub modules.

Maven Reactor

While declaring the modules in parent POM there is no need to bother about their ordering as Maven uses software component called reactor to properly order the modules. In simple-multi, maven builds util module and then app as app depends on util.

 
 

Module POM

Next, let’s explore the util module folder. It contains normal source folders and its own pom.xml. The contents of util/pom.xml is as below

util/pom.xml

<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/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <parent>
       <groupId>org.codetab</groupId>
       <artifactId>simple-multi</artifactId>
       <version>1.0</version>
   </parent>

   <artifactId>util</artifactId>

   <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <maven.compiler.source>1.8</maven.compiler.source>
       <maven.compiler.target>1.8</maven.compiler.target>
   </properties>

   <dependencies>
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
           <version>3.6</version>
       </dependency>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.12</version>
           <scope>test</scope>
       </dependency>
   </dependencies>

</project>

The parent element specifies the coordinates (groupId,artifactId and version) of parent project i.e. simple-multi, while the coordinates of util module is defined using just the artifactId element. There is no need to specify groupId and version for module as they are derived from the parent. The dependencies/dependency block defines the dependencies of util module - commons-lang3 and junit.

Next, let’s see the app/pom.xml which is similar to util/pom.xml except the dependencies block which is as below

app/pom.xml

....
<dependencies>
    <dependency>
        <groupId>org.codetab</groupId>
        <artifactId>util</artifactId>
        <version>1.0</version>
    </dependency>
    ....
</dependencies>

The app module uses methods from util module and for this, the app/pom.xml specifies util as dependency.

To summarize,

The top level project (parent project),

  • defines top level pom.xml which specifies the project coordinates with packaging type as pom and lists its sub modules
  • contains module folders
  • there are no source folders in top level project

Each module folder is nothing but normal maven project directory, however the pom.xml contains

  • parent element specifies module’s parent
  • module coordinates is specified in the artifactId element. The groupId and version are not specified as they are derived from parent coordinates
  • when one module depends on another module, then dependency is specified in dependencies/dependency element.

How to execute multi module project

We can compile, test and package a multi module with

$ mvn clean package

However, running a multi module with maven-exec-plugin is a two-step process. To run the project do as follows

$ cd simple-multi
$ mvn clean install
$ mvn exec:java -pl app -Dexec.mainClass=app.App

The install phase install the modules to local $HOME/.m2 repository and then we run the project. The option -pl app tells exec plugin to use app module and run its class app.App. Without install, build fails as maven is unable to download and resolve the util module dependency.

It is quite cumbersome to install the project in local repository before each run, especially in development phase. Fortunately, we can directly execute a multi module in Eclipse IDE without install. In the later chapter Eclipse and Maven Multi Module, we show how to create multi module project in Eclipse and run it straight away.

In the next tutorial, we extend the simple-multi example and convert it into a hierarchical multi module with multiple parent POM.