Our team has multiple projects; most projects are just libraries. Let's assume for simplicity that the libraries don't depend on each other and there is a single project that uses them, e.g.:
Project Main:
Project Lib-A:
X (3rd-party library)
Project Lib-B:
X (3rd-party library)
To avoid surprises in 'Main', we'd like to make sure that all our own projects are using the same versions of the 3rd-party libraries, so that e.g. both 'Lib-A' and 'Lib-B' are built and tested with the same version of library X.
To achieve this we use a parent pom with <dependencyManagement>
section detailing the versions of all relevant 3rd-party libraries and also their transitive dependencies. This parent pom is inherited by all the projects, i.e. 'Main', 'Lib-A', and 'Lib-B' from the above example. Then each child pom would only use <dependency>
without specifying any version. We also have maven enforcer plugin's dependencyConvergence rule to make sure we have not missed any library conflict in any of our projects.
The problem: increasing the version of X: a developer of 'Lib-A' increases a version of X from 1.0 to 2.0. So he changes X's version in the parent pom, increases the version of parent, releases the parent pom, and notifies guys from 'Main' that they should now use a new parent. The situation becomes like this:
Main - inherits from Parent:2.0 and depends on:
Lib-A:2.0 - inherits from Parent 2.0 and depends on X:2.0
Lib-B:1.0 - inherits from Parent 1.0 and depends on X:1.0
X:2.0 (taken from Parent:2.0 <dependencyManagement> section)
Everything, including 'Main', builds fine, 'maven enforcer plugin' does not detect any conflict because the version of X is clearly specified in the Parent:2.0 from which 'Main' inherits. So we release 'Main'.
Ooops.... Lib-B has never been built with X:2.0. It has great unit tests that would uncover the problem, but we never tried this. We forgot to update Lib-B, try it with X:2.0 and release it. Still 'Main' has been built without problems and maven enforcer plugin has never complained.
Question: we need maven to detect that there are dependencies that inherit from the same artifact but different major versions and fail the build.
In our case the build had to fail since 'Main' and 'Lib-A' inherit from Parent:2.0, but 'Lib-B' inherits from Parent:1.0.
My solution so far (a hack): in addition to inheriting, add an explicit dependency on the parent pom to all out projects (i.e. 'Main', 'Lib-A', and 'Lib-B'):
<dependency>
<artifactId>Parent</artifactId>
<type>pom</type>
<version>${project.parent.version}</version>
</dependency>
Then use <bannedDependencies>
rule of maven enforcer plugin to ban other major Parent versions (we could also use its <dependencyConvergence/>
rule if we want to fail even on minor Parent version conflicts).
Is there a less hacky and cumbersome way to fail on conflicting major versions of parent pom?
May be our entire approach to managing maven dependencies is wrong, what is the recommended way then?
Update:
Tried writing my own rule for maven-enforcer-plugin as suggested by @JF Mayer and described here, before giving up. Reasons:
DependencyGraphBuilder
to detect dependencies on the parent with different major versions. No way! As could be seen with mvn dependency:tree
that also uses this class, DependencyGraphBuilder
does not provide all the dependencies so it can't be used to detect dependencies conflicts. That's why the <dependencyConvergence>
maven enforcer rule is using a super-deprecated DefaultDependencyTreeBuilder
that has been even deleted from the GitHub and everywhere else - not a good choice for a trouble-free custom solution.You can achieve multiple inheritance with profiles: This is not ideal because child projects cannot control the activation of profiles defined in the parent POM. This is not remotely related to inheritance. But you can use them to setup some parameters to turn on/off with a switch (enabling/disabling the profiles).
2. Maven's Multi-Module Project. A multi-module project is built from an aggregator POM that manages a group of submodules. In most cases, the aggregator is located in the project's root directory and must have packaging of type pom.
A "dependencyMangement" section of a parent POMW can be used to manage the version numbers of dependencies in child projects. If a dependency is listed in the dependencyManagement section of a parent, a child's effective POM is not forced to have this dependency in it.
A dependency is libraries you need to get your code to compile. This can be your own code, or libraries such as Apache Commons. A parent contains information, but nothing to actually build, that is shared between a number of your projects.
Maven's Multi-Module Project A multi-module project is built from an aggregator POM that manages a group of submodules. In most cases, the aggregator is located in the project's root directory and must have packaging of type pom.
Let’s learn to create maven projects with parent child relationship. Project creation wizard. Select Project Archtype. Fill details and create project. Now change packaging from jar to pom in pom.xml. Additionally, add project properties and dependencies. Create a new maven project just like you did for parent project.
To match a parent POM, Maven uses two rules: 1 There is a pom file in project’s root directory or in given relative path. 2 Reference from child POM file contains the same coordinates as stated in the parent POM file. More ...
Next, when running mvn package command in the parent project directory, Maven will build and test all three modules. Moreover, Maven Reactor will analyze our project and build it in proper order.
For completeness, my own poor-man's solution:
Add an explicit dependency of type pom to the parent to every project so that maven-enforcer-plugin's <dependencyConvergence>
rule would detect conflicting parent versions. No big deal with this one as we only add this section once and forget about it:
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>${project.parent.artifactId}</artifactId>
<version>${project.parent.version}</version>
<type>pom</type>
</dependency>
<dependencyConvergence>
will fail a build on 'Main' even on 'increment' differences in the parent versions, e.g. 1.0.1 and 1.0.2. In this case, the developer of 'Main' can decide that it's OK to build it despite the parent version conflict, because it's insignificant (this was my original question). mvn -P I-know-what-I-am-doing deploy
.I am not very happy with this solution because of the step 2 that requires the developers of 'Main' to build it with a special profile in case of a parent versions conflict. I'd prefer a solution that always fails on major parent version conflicts but ignores insignificant differences in the parent pom versions automatically, I just don't know how to achieve this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With