Maven dependency resolution between modules during a multi-module project build

Aleksei Bulgak picture Aleksei Bulgak · Jul 20, 2018 · Viewed 7.7k times · Source

I faced with some Maven behavior that I was not expected before. For example we have multi-module project A:

A
|
--- api
--- impl

impl module uses api as a dependency:

<dependency>
   <groupId>examle</groupId>
   <artifactId>api</artifactId>
</dependency>

when I run mvn clean test for whole app maven finishes successfully. when I execute same command for impl module them maven fails with exception like:

[ERROR] Failed to execute goal on project impl: Could not resolve 
dependencies for project A:impl:jar:1.0-SNAPSHOT: Could not find artifact 
A:api:jar:1.0-SNAPSHOT in maven-public

So my question is how maven resolves dependencies that was not build into jar file and pushed into local/remote repositories. In all tutorials it is said that maven looks for dependencies in local repository and if it can not find it then it search in remote repo and it has little bit different behavior for SNAPSHOTS.

But in my case I run test stage and do not build jar file even in target repo

Answer

davidxxx picture davidxxx · Jul 20, 2018

To makes things clear, you noticed this behavior with this command run from the multi-module project :

mvn clean test

but you would have the same behavior, that is : getting the dependencies resolved and usable between modules without previously installing them in the local repository with any phase you run such as :

mvn test
mvn compile
mvn package

In fact the Maven documentation about this point is not explicit.

You can read in the Guide to Working with Multiple Modules :

The Reactor

The mechanism in Maven that handles multi-module projects is referred to as the reactor. This part of the Maven core does the following:

  • Collects all the available modules to build

  • Sorts the projects into the correct build order

  • Builds the selected projects in order

You can guess that if the order of the modules matters for the Maven build, it probably means that the build of a module relies on the build of dependent modules that were previously built. That explains the sort done by the reactor if the order you specified in <modules> is not correct in terms of dependencies (the correct order is that the used dependency has to be declared before the user dependency).

There are of course some uses cases where you want to install a maven artifact into a local repository such as (not exhaustive) :

  • you don't use a multi module project
  • the build of the whole multi module project is long. You want spare time by building just some specific artifacts, so without building the multi-module project.
  • one or several modules included in the multi module project is shared/used by other projects.

By executing your maven build command with the -X flag (the debug flag) you will see that Maven computes the dependencies between module for each module build.
For example with your example you should see for the impl build :

DEBUG] === PROJECT BUILD PLAN
================================================

[DEBUG] Project:       A:impl:0.0.1-SNAPSHOT

[DEBUG] Dependencies (collect): []

[DEBUG] Dependencies (resolve): [compile, test]

And a little later the detection of the inter-module dependency :

[DEBUG] A:impl:jar:0.0.1-SNAPSHOT
[DEBUG]    A:api:jar:0.0.1-SNAPSHOT:compile

Here is a more detailed extract of :

[DEBUG] =======================================================================
[DEBUG] Dependency collection stats: {ConflictMarker.analyzeTime=23166, ConflictMarker.markTime=13490, ConflictMarker.nodeCount=2, ConflictIdSorter.graphTime=31377, ConflictIdSorter.topsortTime=6158, ConflictIdSorter.conflictIdCount=1, ConflictIdSorter.conflictIdCycleCount=0, ConflictResolver.totalTime=51611, ConflictResolver.conflictItemCount=1, DefaultDependencyCollector.collectTime=368903, DefaultDependencyCollector.transformTime=134014}
[DEBUG] A:impl:jar:0.0.1-SNAPSHOT
[DEBUG]    A:api:jar:0.0.1-SNAPSHOT:compile

Consequently, the plugins executed during the build of the impl will also have a classpath including the compiled classes of the api module.
For example the debug traces of the compiler plugin execution show :

[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ impl ---
...
[DEBUG]   (f) classpathElements = [C:\...\test-parent-pom\impl\target\classes, C:\...\test-parent-pom\api\target\classes]