How to force maven dependency version via dependencyManagement in parent pom? - maven

Let's say B:1.0.1 has transitive dependency A:1.0.1, but the child project is supposed to depend on A:1.0.2 (with intentional overriding transitive dependencies).
It is easy to discover that the order of dependencies in <dependencyManagement> affect versions overriding, so adding A:1.0.2 in the child pom just before B:1.0.1 would force using A:1.0.2 even as a dependency for B:1.0.1.
In this case I'm looking for a way to declare A:1.0.2 in the parent pom, and remove boilerplate from all its children. Unfortunately, the following setup leads to using both versions in the final artifact: A:1.0.1 (comes as a dependency of B:1.0.1) and A:1.0.2 (comes from the explicit declaration in the parent pom).
How to force using version A:1.0.2 in all child projects, keeping the declaration in the parent?
Parent pom:
<groupId>my-group</groupId>
<artifactId>my-parent</artifactId>
<version>0.0.1</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>g</groupId>
<artifactId>A</artifactId>
<version>1.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Child pom:
<parent>
<groupId>my-group</groupId>
<artifactId>my-parent</artifactId>
<version>0.0.1</version>
</parent>
<artifactId>my-child</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>g</groupId>
<artifactId>A</artifactId>
<!-- version 1.0.2 comes from the parent pom -->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>g</groupId>
<artifactId>B</artifactId>
<version>1.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

You are using the dependencyManagement incorrectly.
If A and B are jar artifacts, you should not have the tags
<type>pom</type>
<scope>import</scope>
These are for BOMs only.

Related

Should we use <dependencyManagement> or <properties> to manage versions in Maven?

There are 2 ways to manage versions in multi-project solutions, e.g. Java project with microservices:
Dependency management
In parent pom.xml you define a long list of depencies used across child projects
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>
</dependencyManagement>
and in child pom.xml you just don't specify versions
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Version placeholders
In parent pom.xml you define properties like
<properties>
<driver.version>4.9.0-scylla-1</driver.version>
<spring-boot.version>3.0.1</spring-boot.version>
<spring-cloud.version>2022.0</spring-cloud.version>
<lombok.version>1.18.24</lombok.version>
</properties>
then in child pom.xml you must specify version in a way
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
Which is the preferred way to keep dependency versions consistent in a microservice environment? What are pros and cons of these two ways? The second way seems more elastic because each of child projects may select other version of dependency and you may build project with another version of some dependency from commandline.

Maven doesn't run unit tests in a multi-module project with Spring Boot

Maven supports multi-module applications by having the child modules point to the parent module, but Spring Boot wants it's parent to be
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
According to the Spring docs I can still do this (use a maven parent-child relationship) if I add dependency management like this:
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
instead of the Spring Boot parent.
I've done this, but now my unit tests don't run when I run mvn from the command line. The unit tests still work from Intellij, but something in the new parent-child / changes is confusing mvn and keeping it from running tests.
Here's a pom.xml from a module that no longer runs its tests:
<project xmlns=...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>abcd</groupId>
<artifactId>abcd.parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>entities</artifactId>
<name>entities</name>
<description>Entities and Repositories</description>
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
This module is a library module that does not contain a #SpringBootApplication but the tests ran before converting to the maven parent-child structure.
Edit:
This project has both junit 4 and junit-jupiter-api 5.5.2 in its dependencies. When using org.junit.jupiter.api.Test (5.5.2) no tests run. When using org.junit.Test (4.12) the test runs but the Autowired repository doesn't get injected (null). (The Autowired repository is in another module.)
Spring doesn't handle dependencies correctly when using a normal maven child-parent structure.

How a maven dependecy can be defined without version tag?

I have a pom which works without defining dependency version in pom works fine and another one without dependency version which does not work.
The one which works:
<project>
<parent>
<artifactId>artifact-parent</artifactId>
<groupId>group-parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
</dependency>
</dependencies>
</project>
This one which does not work:
<project>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
</dependency>
</dependencies>
</project>
The only thing differ in these two seems to be:
<parent>
<artifactId>artifact-parent</artifactId>
<groupId>group-parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
Second one does not work it seems fine to me but my question is why the first one works ?
Quoted from maven pom reference :
This trinity represents the coordinate of a specific project in time,
demarcating it as a dependency of this project.
So my question is how the first one is working?
The main thing to notice here is:
<parent>
<artifactId>artifact-parent</artifactId>
<groupId>group-parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
Version of the dependency looks like to be defined in the parent pom. This can be something like this:
<project>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Again quoting from the doc :
This is because the minimal set of information for matching a
dependency reference against a dependencyManagement section is
actually {groupId, artifactId, type, classifier}.
Here we did not need to define the {type, classifier} as it is same as default value which is as follows :
<type>jar</type>
<classifier><!-- no value --></classifier>
If this value differ form the default, you need to define it explicitly in both parent pom and child pom.

Should you use the parent if you are using the spring platform bom?

Some dependency versions are not in so I've added the spring platform BOM, is the parent declaration still useful?
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.1.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
I personally prefer to use platform-bom as a parent, i.e.
<parent>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>1.1.1.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
In this way I don't have to define spring-boot version number and it is automatically updated with newer version of spring platform and I don't have to worry about any inconsistencies.
See http://docs.spring.io/platform/docs/1.1.1.RELEASE/reference/htmlsingle/#appendix-dependency-versions for complete list of all managed dependencies.
EDIT: As pointed out by Andy Wilkinson, spring platform inherits spring-boot-starter-parent so all "sensible defaults" as described in http://docs.spring.io/spring-boot/docs/1.2.1.RELEASE/reference/htmlsingle/#using-boot-maven apply as well.
There is a important difference between importing a BOM (in the dependencyManagement section) and using a parent
The BOM imported in dependencyManagement only provides defaults for dependencies, but a Parent-way include the other sections too (plugins, plugin-managent, dependencies, dependencyManagement...)
So when you remove the parent spring-boot-starter-parent then you have to copy the the plugin-managent stuff you need first.

Maven Plugin for Version Management

I am looking for a possibility to manage my maven project versions.
I have some maven modules in my maven project and some of these modules are depending on others of these modules.
I want to define the version to work with globally for every module or dependency.
Is this somehow possible?
Something like
globalVersion=2.0
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>${globalVersion}</version>
But as i said, not in each single pom. I mean globally for all my poms in my maven modules.
(I assume you have a parent pom common for all your modules.)
define a property in the parent pom:
<properties>
<globaleVersion>1.0.0</globalVersion>
</properties>
And define a <dependencyManagement> section in the parent pom too:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>${globalVersion}</version>
</dependency>
<dependency>
<groupId>A</groupId>
<artifactId>A</artifactId>
<version>${globalVersion}</version>
</dependency>
</dependencies>
</dependencyManagement>
And in you modules define dependencies without specifying the version (maven will find it from the dependencyManagement section of the parent)
<dependencies>
<dependency>
<groupId>test</groupId>
<artifactId>test</artifactId>
</dependency>
<dependency>
<groupId>A</groupId>
<artifactId>A</artifactId>
</dependency>
</dependencies>

Resources