Maven dependencyManagement: inherit dependency version from parent - maven

I want my dependency in dependencyManagement block to inherit a version from spring-boot-parent's dependencyManagement block, but add exclusion to it, so that I don't need to specify that exclusion in each of child modules.
My parent pom inherits from spring-boot-parent:
<artifactId>my-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>???</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencyManagement>
child pom inherits my-parent:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
I tried several approaches:
When I replace ??? with ${parent.version}, in child module this version is resolved to be my-parent's version: 1.0.0-SNAPSHOT, which is incorrect.
When I replace ??? with ${parent.parent.version}, maven breaks, as it doesn't support such properties
I can fix ??? to be 2.0.1.RELEASE and this will work fine, but if I update the version of spring boot, I have to remember to update the version of this dependency also, which is non-intuitive
I cannot extract 2.0.1.RELEASE as a property in my-parent and use that property as parent version, as maven does not support that.
I could've used property with value 2.0.1.RELEASE inherited from spring-boot-parent pom, but there is none such property, as far as I can see.
Is there a nice way to achieve what I want?

See Possible to have the parent version as Property to give to the children? - the version used to declare your parent can't be repurposed in a way that says "make this reference fixed to mean its interpretation at this point in the graph". Strings are strings and placeholders are placeholders; there's no built-in facility to have some kind of in-between thing. If the parent uses a property to define a dependency version you can reference that property; if it uses a fixed string (as in your case) then you'll need to repeat something in order to define an exclusion. As referenced in the linked question, a common way is something like this:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<properties>
<spring.boot.version>2.0.1.RELEASE</spring.boot.version>
</properties>
<dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
...
You still have to repeat the value in two fields -- so you have to make very sure that you always keep those in sync (parent version and property).
However, the increasingly-popular way to manage this is to declare those versions in a BOM (a "bill-of-materials" pom with the specific purpose of keeping a set of components aligned). Then the dependencyManagement stuff can be imported instead of inherited. Spring Boot has been doing this for a while now; in fact the parent in your example doesn't actually declare the dependencies itself, they come from a different artifact. So instead of inheriting from the parent, you could do something like this (I might have the sequence of these backwards -- I can't remember whether first or last wins):
<properties>
<spring.boot.version>2.0.1.RELEASE</spring.boot.version>
</properties>
<dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
...
... now, if there are other reasons (beyond just dep mgmt) you wanted to inherit from spring-boot-starter-parent then this won't really help your problem; you'll still need to duplicate. But I suspect that as your project matures you'll want to have a parent of your own devising anyway.

Related

Maven override parent dependency with specific dependency

I want to override parent spring-data-elasticsearch dependency with specific one.
pom.xml:
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/>
</parent>
...
<dependencies>
...
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.1.1</version>
</dependency>
</dependencies>
...
with this configuration i have 7.17.6 elasticsearch in libraries. i need 7.12.1 When i change the version to higher or lower version nothing changes, but when i change the paren version to 2.5.2 the elasticsearch version in libraries become 7.12.1 . My question is: How do i change the version of the dependecy without changing the parent version?
I have tried to exclude the dependency with tag but that didn't help me.
It seems that spring-data-elasticsearch/4.1.1 is dependent on elasticsearch up to v7.17.8 (not v7.12.1)
To override the dependency to elasticsearch in the parent you should use the dependencyManagement tag, as follows to use elasticsearch v7.12.1.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.12.1</version>
</dependency>
</dependencies>
</dependencyManagement>
Putting the dependency outside the dependencyManagement tag will work but is incorrect.

Flatten Plugin: Resolve dependencyManagement of bom without inherited

I created an example project for this problem: https://github.com/robeatoz/flatten-resolve-dependency-management-without-inherited
Following project structure is given:
foo-build as the parent for all modules
foo-module-a as child module
foo-module-b as child module
foo-module-c as child module
foo-dependencies as bom
I used the flatten-maven-plugin and the property revision for CI friendly builds in all modules:
<groupId>stack.overflow</groupId>
<artifactId>foo-build</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<properties>
<revision>0.1-SNAPSHOT</revision>
</properties>
The parent (foo-build) manages one external dependency:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>external.artifact</groupId>
<artifactId>module-managed-in-parent</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
The bom (foo-dependencies) manages the foo dependencies:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-a</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-b</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-c</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>
I want that the flattened pom of the bom contains only the resolved foo dependencies without the dependencies managed by the parent (foo-build) like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-a</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-b</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-c</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
How do you have to configure the flatten-maven-plugin to achieve this?
I already tried <flattenMode>bom</flattenMode>, but then the flattened pom does not resolve the versions:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-a</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-b</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-c</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>
With the following configuration
<pomElements>
<properties>remove</properties>
<dependencyManagement>resolve</dependencyManagement>
</pomElements>
the flattened pom contains the managed dependency of the parent:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-a</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-b</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>stack.overflow</groupId>
<artifactId>foo-module-c</artifactId>
<version>0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>external.artifact</groupId>
<artifactId>module-managed-in-parent</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
It would be simpler to change your approach to more client-centric:
Make foo-dependencies a root project (./pom.xml).
with only foo-* dependencies in dependency management section
modules list with a single foo-build module (would be truncated by flatten plugin)
generic project properties (would be truncated by flatten plugin)
Make foo-build an intermediate project (./foo-build/pom.xml).
with third-party dependencies in dependency management section
with build-specific properties or profiles, required by your project (if any)
Retain foo-module-* leaf modules with foo-build as a parent
If you insist on plain project structure (./foo-module-*/pom.xml), you can use relativePath to point parent module, e.g.:
<parent>
<groupId>stack.overflow</groupId>
<artifactId>foo-build</artifactId>
<version>${revision}</version>
<relativePath>../foo-build/pom.xml</relativePath>
</parent>
<artifactId>foo-module-a</artifactId>
This way you will receive:
clear foo-dependencies as you wish;
zero copy-paste for foo-* dependencies;
flexibility to build whatever and however you like in foo-build without side-effects on foo-dependencies (neither now nor in the future).
I have like the exact same usecase and I solved it with the following configuration.
You need to apply a very specific configuration to the flatten-plugin within the pom file of the BOM module:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<configuration>
<updatePomFile>true</updatePomFile>
<pomElements>
<dependencyManagement>expand</dependencyManagement>
</pomElements>
</configuration>
</plugin>
expand means that the dependencyManagement block will be replaced with the one from the effective pom where all references are properly resolved. updatePomFile is necessary because otherwise by default, the flattened pom would not be published for poms with <packaging>pom</packaging>
Here are the relevant parts from the flatten-plugin's documentation:
updatePomFile: https://www.mojohaus.org/flatten-maven-plugin/flatten-mojo.html#updatePomFile
Explanation for expand: https://www.mojohaus.org/flatten-maven-plugin/apidocs/org/codehaus/mojo/flatten/ElementHandling.html#expand
You don't need to change the version with ${revision}, use ${project.version}, take a try

Maven issue when overriding an environment specific systemPath property

I experienced issues with a maven build that does not behave the same way if done on Windows (like they were done in the past) or Linux (like I want to do them now).
I want to build a project that has a dependency on another project that pom that itself imports a pom that contains a Windows path.
my project | other project
|
mybuild -------|------> pom --------> pom with systemPath
dependency import
|
But in a nutshell, here is my pom:
<groupId>test.properties</groupId>
<artifactId>buildme</artifactId>
<version>1.0-SNAPSHOT</version>
...
<dependencies>
<dependency>
<groupId>test.properties.installme</groupId>
<artifactId>module</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
</dependencies>
And I depend on a pom that looks like this (not under my control)
<groupId>test.properties.installme</groupId>
<artifactId>module</artifactId>
<version>1.0-SNAPSHOT</version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>test.properties.installme</groupId>
<artifactId>dependency</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
and the problem lies in this last pom (not under my control):
<modelVersion>4.0.0</modelVersion>
<groupId>test.properties.installme</groupId>
<artifactId>dependency</artifactId>
<version>1.0-SNAPSHOT</version>
...
<properties>
<com.sun.tools.path>D:/java/jdk1.8.0_65/lib/tools.jar</com.sun.tools.path>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${com.sun.tools.path}</systemPath>
</dependency>
</dependencies>
</dependencyManagement>
I have no control on the other project in question. I totally agree that a refactoring to use environment variable in place of the hard coded paths would solve my problem.
But instead the Windows path is defined in a property. One would think that overriding the value of the property depending on my platform would be enough. But it is not.
Unfortunately in this precise case case maven seems to behave to behave poorly.
Before applying any property override in any form (in settings.xml, -Dproperty=, redefinition in root pom), maven starts building the effective pom. And during that step, if it finds the pattern I mentioned above (a dependency on another pom that itself imports a pom that contains a Windows path), then it says:
The POM for <groupId>:<artifactId>:jar:<version> is invalid, transitive dependencies (if any) will not be available
As a consequence, my project needs to explicitly define all the dependencies of the second project. And I cannot rely on transitive dependencies which gives me a lot of trouble.
In order to illustrate the issue, I created a minimal example showing the problem. It can be found here:
https://github.com/fabricepipart/test-properties
Do you see any workaround for this?
Any way to override the value of the property and still benefit from the maven transitive dependencies?
Thanks a lot

Maven - Get non-inheritable pom version.

Assume a parent pom with version 1.0 and a child pom with version 2.0.
Now I define a dependency like this in the parent.
<dependency>
<groupId>somedep</groupId>
<artifactId>somedep</artifactId>
<version>${project.version}</version>
</dependency>
In the parent, the version evaluates to 1.0, but in child it evaluates to 2.0. Is there a way, in which I can make it evaluate to 1.0 in child too (Ofcourse no version harcoding in parent is permitted)?
EDIT:
If we look in http://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Project_Inheritance, we can see that:
One factor to note is that these variables are processed after
inheritance as outlined above. This means that if a parent project
uses a variable, then its definition in the child, not the parent,
will be the one eventually used.
Which means that you can't rely on variables which get dynamically resolved in parent and child and are of the type `${project.version} if your child and parent have different versions, which is generally not what you'd want but very specific to your case. I guess what you are left over with is to hardcode the dependency in the parent using something like depedencyManagement:
Parent:
<project>
<groupId>example</artifactId>
<artifactId>masta</artifactId>
<version>1.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>someDep</groupId>
<artifactId>someDep</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>someDep</groupId>
<artifactId>someDep</artifactId>
</dependency>
</dependencies>
</project>
And child:
<project>
<parent>
<groupId>example</artifactId>
<artifactId>masta</artifactId>
<version>1.0</version>
</parent>
<groupId>example.masta</artifactId>
<artifactId>child</artifactId>
<version>2.0</version>
<dependencies>
<dependency>
<groupId>someDep</groupId>
<artifactId>someDep</artifactId>
</dependency>
</dependencies>
</project>
or, indeed, use ${parent.version} explicitly in the child. One thing to note is I'd generally not have aggregator projects like parent introduce any dependencies but have only dependencyManagement.
This is the second approach, without dependency management:
Parent:
<project>
<groupId>example</artifactId>
<artifactId>masta</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>someDep</groupId>
<artifactId>someDep</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
And child:
<project>
<parent>
<groupId>example</artifactId>
<artifactId>masta</artifactId>
<version>1.0</version>
</parent>
<groupId>example.masta</artifactId>
<artifactId>child</artifactId>
<version>2.0</version>
<dependencies>
<dependency>
<groupId>someDep</groupId>
<artifactId>someDep</artifactId>
<version>${parent.version}</version>
</dependency>
</dependencies>
</project>
But I guess none of the approaches actually fully satisfies what you want.

Maven modules are trying to use parent dependencies

I have a maven project where the parent module has a lib directory containing various jars that are necessary for compilation, but aren't included in the final product. When I try to get the children modules to build it fails. It says "The following artifacts could not be resolved" then eventually says "Could not find artifact local_dependency at C:\path\to\project\modules\module_name\lib\local_dependency.jar".
The children modules do not depend on the libraries that the parent uses, however it still wants to include them. Is there an option I need to set to prevent this?
Parent Pom snippet:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<currentVersion>1.0.0</currentVersion>
</properties>
<groupId>com.project</groupId>
<artifactId>project_artifact</artifactId>
<packaging>pom</packaging>
<version>${currentVersion}</version>
<modules>
<module>modules/module_name</module>
</modules>
<dependencies>
<dependency>
<groupId>group.id</groupId>
<artifactId>local_dependency</artifactId>
<version>1.0</version>
<systemPath>${basedir}/lib/local_dependency.jar</systemPath>
<scope>system</scope>
<optional>true</optional>
</dependency>
</dependencies>
Child pom snippet:
<parent>
<groupId>com.project</groupId>
<artifactId>project_artifact</artifactId>
<version>${currentVersion}</version>
<relativePath>../../</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>net.some.dependency</groupId>
<artifactId>artifact_name</artifactId>
<version>1.0.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.project</groupId> <!-- The child depends on the parent for the parent's API-->
<artifactId>project_artifact</artifactId>
<version>${currentVersion}</version>
<type>jar</type>
</depdencency>
</dependencies>
So from this, the child pom will attempt to include group.id:local_dependency from project_base/modules/module_name/lib/local_dependency.jar but it doesn't exist and doesn't need to exist.
You can exclude specific transitive dependencies in the dependency declaration. In your case, the following change in the child pom's dependency on the parent should get the build working:
<dependency>
<groupId>com.project</groupId> <!-- The child depends on the parent for the parent's API-->
<artifactId>project_artifact</artifactId>
<version>${currentVersion}</version>
<type>jar</type>
<exclusions>
<exclusion>
<groupId>group.id</groupId>
<artifactId>local_dependency</artifactId>
</exclusion>
</exclusions>
</dependency>
The child inherits the parent's dependencies, whether or not you include the dependency explicitly. Two possible ways to resolve the issue are:
Don't build any jar artifact in the parent - create a sub-module for this and use the sub-module as a dependency in its siblings.
Use a fixed path (not relative to ${basedir}, since this changes in each module build, which tries to resolve the location anew). If you always build from the parent's directory, you could use ${user.dir}.

Resources