Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maven: ensure same major parent pom version in multi-project development

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:

  1. First, the parent pom information is not available from the dependencies, at least not from the nodes built by maven's DependencyGraphBuilder
  2. OK, I've added my parent poms as explicit dependencies to the children and tried to use this 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.
like image 561
Alexander Avatar asked May 14 '20 11:05

Alexander


People also ask

Can a Maven POM have 2 parents?

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

What is the packaging type of an aggregator pom in a multi-module Maven project?

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.

Can parent POM have dependencies?

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.

What is the difference between parent and dependency in Maven?

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.

What is multi module project in Maven?

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.

How to create Maven projects with parent child relationship?

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.

How to match parent pom with child pom in Maven?

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

What happens when you run MVN in parent project directory?

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.


1 Answers

For completeness, my own poor-man's solution:

  1. 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>
    
  2. <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).
    So he builds 'Main' with some special profile that excludes the dependency on the parent:
    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.

like image 75
Alexander Avatar answered Oct 04 '22 22:10

Alexander