27

I have a Maven project with multiple modules and sub-modules and i want to compile it at once, i.e. using only one call to "mvn clean install".

For a basic project, the following structure would work:

.
├── modules
│   ├── moduleA
│   │   └── pom.xml <--- Module A POM
│   ├── moduleB
│   │   └── pom.xml <--- Module B POM
│   └── pom.xml     <--- Super POM (at the root of "modules" folder)
└── pom.xml         <--- Aggregator POM

With the aggregator being:

<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>org.test</groupId>
    <artifactId>aggregator</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>

    <modules>
        <module>modules</module>
        <module>modules/moduleA</module>
        <module>modules/moduleB</module>
    </modules>

</project>

The Super POM:

<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>org.test</groupId>
    <artifactId>super-pom</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

Module A's POM:

<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>

    <artifactId>moduleA</artifactId>

    <parent>
        <groupId>org.test</groupId>
        <artifactId>super-pom</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

</project>

Module B is similar.

When at the root of the project, running "mvn clean install" command gives (after cleaning up the .m2/repository folder):

[...]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] super-pom .......................................... SUCCESS [  0.450 s]
[INFO] moduleA ............................................ SUCCESS [  1.746 s]
[INFO] moduleB ............................................ SUCCESS [  0.029 s]
[INFO] agregator .......................................... SUCCESS [  0.006 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

Now if I want something that is more complex (but still using one super POM), such as:

.
├── modules
│   ├── lib1
│   │   ├── moduleA1
│   │   │   └── pom.xml
│   │   ├── moduleB1
│   │   │   └── pom.xml
│   │   └── pom.xml      <--- lib1 aggregator POM
│   ├── lib2
│   │   ├── moduleA2
│   │   │   └── pom.xml
│   │   ├── moduleB2
│   │   │   └── pom.xml
│   │   └── pom.xml      <--- lib2 aggregator POM
│   └── pom.xml          <--- Super POM
└── pom.xml              <--- Aggregator POM

With the root POM being:

<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>org.test</groupId>
    <artifactId>agregator</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>

    <modules>
        <module>modules</module>
        <module>modules/lib1</module>
        <module>modules/lib2</module>
    </modules>

</project>

Lib1 POM:

<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>org.test</groupId>
    <artifactId>lib1-agregator</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>

    <modules>
        <module>moduleA1</module>
        <module>moduleB1</module>
    </modules>

</project>

And, for instance moduleA1 POM:

<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>

    <artifactId>moduleA1</artifactId>

    <parent>
        <groupId>org.test</groupId>
        <artifactId>super-pom</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

</project>

Maven does not manage to resolve the POM files (after cleaning up the .m2/repository folder):

[INFO] Scanning for projects...
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[WARNING] 'parent.relativePath' of POM org.test:moduleA1:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib1/moduleA1/pom.xml) points at org.test:lib1-agregator instead of org.test:super-pom, please verify your project structure @ line 7, column 13
[FATAL] Non-resolvable parent POM for org.test:moduleA1:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13
[WARNING] 'parent.relativePath' of POM org.test:moduleB1:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib1/moduleB1/pom.xml) points at org.test:lib1-agregator instead of org.test:super-pom, please verify your project structure @ line 7, column 13
[FATAL] Non-resolvable parent POM for org.test:moduleB1:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13
[WARNING] 'parent.relativePath' of POM org.test:moduleA2:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib2/moduleA2/pom.xml) points at org.test:lib2-agregator instead of org.test:super-pom, please verify your project structure @ line 7, column 13
[FATAL] Non-resolvable parent POM for org.test:moduleA2:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13
[WARNING] 'parent.relativePath' of POM org.test:moduleB2:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib2/moduleB2/pom.xml) points at org.test:lib2-agregator instead of org.test:super-pom, please verify your project structure @ line 7, column 13
[FATAL] Non-resolvable parent POM for org.test:moduleB2:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13
 @ 
[ERROR] The build could not read 4 projects -> [Help 1]
[ERROR]   
[ERROR]   The project org.test:moduleA1:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib1/moduleA1/pom.xml) has 1 error
[ERROR]     Non-resolvable parent POM for org.test:moduleA1:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13 -> [Help 2]
[ERROR]   
[ERROR]   The project org.test:moduleB1:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib1/moduleB1/pom.xml) has 1 error
[ERROR]     Non-resolvable parent POM for org.test:moduleB1:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13 -> [Help 2]
[ERROR]   
[ERROR]   The project org.test:moduleA2:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib2/moduleA2/pom.xml) has 1 error
[ERROR]     Non-resolvable parent POM for org.test:moduleA2:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13 -> [Help 2]
[ERROR]   
[ERROR]   The project org.test:moduleB2:[unknown-version] (/Users/ben/IdeaProjects/ComplexMavenStructure/modules/lib2/moduleB2/pom.xml) has 1 error
[ERROR]     Non-resolvable parent POM for org.test:moduleB2:[unknown-version]: Could not find artifact org.test:super-pom:pom:1.0.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 7, column 13 -> [Help 2]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableModelException

I need to first build the super pom:

cd modules/
mvn clean install

And only then will it compile:

[INFO] Reactor Summary:
[INFO] 
[INFO] super-pom .......................................... SUCCESS [  0.271 s]
[INFO] moduleA1 ........................................... SUCCESS [  1.202 s]
[INFO] moduleB1 ........................................... SUCCESS [  0.027 s]
[INFO] lib1-agregator ..................................... SUCCESS [  0.006 s]
[INFO] moduleA2 ........................................... SUCCESS [  0.027 s]
[INFO] moduleB2 ........................................... SUCCESS [  0.022 s]
[INFO] lib2-agregator ..................................... SUCCESS [  0.007 s]
[INFO] agregator .......................................... SUCCESS [  0.007 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

How would you structure the project to make it work without this two-steps compilation?

[EDIT]: as discussed in the comments, adding the relativePath of the Super POM to the modules POMs would make the code compile with one command line.

However, if I want to distribute only lib1 to some developers, even though my super POM is on a Maven repository, lib1 would not compile without the whole project structure. This makes the project less modular.

Ben
  • 6,321
  • 9
  • 40
  • 76
  • Lib1 and 2 have to be defined as Modules in your parent-pom ?! Or is there a reason not to ? Maven will not include transitive dependencies in the reactor-build. – Hendrik Jander Jan 27 '16 at 10:36
  • The idea is to compile the whole project with one command line. This is why I added both lib1 and lib2 to the root aggregator. Adding in this POM the end modules directly (e.g. modules/lib1/moduleA1) does not change the behaviour and Maven fails to build as well. – Ben Jan 27 '16 at 10:40
  • Your issue is not about the reactor build , but about your parent-pom. IMO you have to edit all parent-pom definitions to add the Element. [See Example 2 here](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Project_Inheritance) . – Hendrik Jander Jan 27 '16 at 11:00
  • Adding relativePath indeed works. I find it however inconvenient when you add new modules/submodules and your structure gets more complicated. – Ben Jan 27 '16 at 11:09
  • Thats true, but Eclipse as well as IntelliJ have good tooling for that case. You have to add a Maven *Module* – Hendrik Jander Jan 27 '16 at 11:11
  • Another caveat, is if I distribute only lib1 to another developer. In that case, the relativePath is not compatible with retrieving the super pom from a remote maven repository. – Ben Jan 27 '16 at 18:37
  • for that you can use – Hendrik Jander Jan 28 '16 at 22:37
  • link - jeejava.com/ – user3470953 May 09 '17 at 02:52

1 Answers1

32

Let me start like you did, but I name the things a little bit differently:

.
├── modules-root
│   ├── moduleA
│   │   └── pom.xml <--- Module A POM
│   ├── moduleB
│   │   └── pom.xml <--- Module B POM
│   └── pom.xml     <--- modules root
└── pom.xml         <--- project-root

Let's start with the project-root, which will look like this:

<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>org.test</groupId>
    <artifactId>project-root</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <modules>
       <module>modules-root</module>
    </modules>

</project>

The module-parent will look like this. I will emphasis that this contains only the reference to the moduleA and moduleB and will inherit from the project-root:

<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>

   <parent>
        <groupId>org.test</groupId>
        <artifactId>project-root</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <groupId>org.test.module</groupId>
    <artifactId>module-parent</artifactId>
    <packaging>pom</packaging>

    <modules>
        <module>moduleA</module>
        <module>moduleB</module>
    </modules>
</project>

moduleA will look like this. Pay attention that this will inherit from module-parent (parent) which is exactly one level above...

<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>

    <parent>
        <groupId>org.test.module</groupId>
        <artifactId>module-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>moduleA</artifactId>
    <packaging>..</packaging>

    <....other dependencies..>
</project>

If you use this kind of structure you can simply go to the project-root level and do:

mvn clean install

Furthermore afterwards you can use things like this:

mvn -pl moduleA ...

...to build only moduleA (but stay at the project-root level...).

module-parent might look like wasted or superfluous in this example, but if you get more modules you could define their supplemental dependencies via dependencyManagement or may changing plugin configurations via pluginManagement which will only be used in this sub area (moduleA, moduleB, ...). If your project becomes larger, you will get more module-parents in parallel..which contain different parts of your applications...and different intentions which can be achieved with this structure.

One more thing is to mention; I have changed the groupId from org.test to org.test.module in the module-parent. This is sometimes useful if you have a large number of modules cause the groupId represents the folder structure in your repository (as java packages do in a Java project)... this give you an better overview...

The project-root is the location to define the overall usable dependencies via dependencyManagement etc....and the used plugins which should be defined via pluginManagement... or may be using maven-enforcer-plugin to define overall project rules...

Typical scenarios for this kind of structure are Java EE projects or other large projects (may be with 300...or 1000 modules yes they exist)...

If you get more modules you can use the multi-thread capability of maven and build your project with:

mvn -T 4 clean install 

from the project-root which reduces the build time dramatically.

Malvon
  • 1,591
  • 3
  • 19
  • 42
khmarbaise
  • 92,914
  • 28
  • 189
  • 235
  • In your example, you mix inheritance and composition in your POM files. I though it was better to separate those in two distinct POM files (which is what I usually do). Isn't it? – Ben Jan 28 '16 at 14:03
  • Where do i use composition? I only used inheritance...from parent to child...Wasn't the question to compile all in once? This will exactly do this...and has other advantages..single release point etc.... – khmarbaise Jan 28 '16 at 15:17
  • 1
    Multi-modules definition in one POM is composition. But you indeed have a point: it compiles all in one command line. – Ben Jan 28 '16 at 15:45
  • Just wanted to say thank you as this helped me setup my first multi module project with ease..... – Grim May 04 '17 at 01:28
  • Nice, clean layout. Thank you! – Jesús Zazueta May 17 '17 at 12:24
  • Where is module-parent defined? I only see it being pointed out as a parent but not the actual definition of it. – why_vincent Dec 17 '17 at 16:42
  • 1
    But why don't the artifactIds in and the actual module artifactIds match up? e.g. in the text you say `module-parent` will inherit from `project-root`, but then in code you have `modules-root` inherits from `super-pom` and there is no project with artifactId `super-pom` defined?! – frido Sep 22 '18 at 13:22