Maven internal library pom version management - maven

Are there any tools and/or best practices to help with managing a large set of internal libraries versions across many dependent projects. Allow me to elabore,
Say you have products A, B, C.
These products depend on libraries X, Y, Z, and there are dependencies within those as well.
In this world, we like to keep all versions on the latest released version of all dependencies (no snapshot dependencies except in working copies). This allows us to release the product at any time and force all projects to be run in CI (Hudson).
The issue is in keeping the pom.xml's all up-to-date. Currently, we use a custom maven plugin that, as part of each release, searches our SCM for pom.xml's that depend on the project being released and updates it. This is similar to the versions-maven-plugin except that you don't need a working copy of each project to do it.
There has got to be a better way. What do other teams do about many shared libraries across many projects? What is the best way to organize this? Multi-module works in some cases, but most of our libraries are fairly independent and used by too many other projects to (a) decide which multi-module it would belog to and (b) the hierarchy that would work for this.

managing dependencies in Maven works fine with dependencyManagement - I think you are familiar with it. You can outsource this dependencyManagement to a dedicated POM: a so called BOM POM (see http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html).
In your case this BOM POM might be shared by your products A, B and C. Due to that fact you should define a Maven project just containing this BOM POM with all the dependency management for thirdparty software X, Y and Z. This project can be released and you are able to add a dependency on your BOM POM with the scope import.
Your BOM POM:
<project>
<groupId>com.acme</groupId>
<artifactId>my-thirdparty-bom-pom</artifactId>
<version>1.0-SNAPSHOT</version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>X</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>...</groupId>
<artifactId>Y</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>...</groupId>
<artifactId>Z</artifactId>
<version>...</version>
</dependency>
</dependencies>
<dependencyManagement>
...
</project>
Sample import for project A
<project>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>my-thirdparty-bom-pom</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>import</scope>
<dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>Y</artifactId>
<!-- the version is managed by the thirdparty bom imported above -->
</dependency>
</dependencies>
...
</project>

The closest thing is to use Dependency Version Ranges and then in a profile override your pom's repositories changing snapshots to false, making sure to include your profile during release with "-P".
<dependency>
<groupId>a</groupId>
<artifactId>a</artifactId>
<version>[3.8,4.0)</version>
</dependency>
...
<profiles>
<profile>
<id>your-release</id>
<repositories>
<repository>
...
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</profile>
<profiles>
Another pattern is the "bom" whereby you create a pom (packaging: pom) that declares a list of dependencies to make management easier (less to change).

Related

Retrieving the top-level version from sub modules in Maven

This is a version of the question posted in Maven: retrieving the main module version from a sub-module, but I can't figure out how to apply the answer. I'm trying this on Maven 3.6.3. I've posted the repository this can be played around with at this GitHub project
I have a top-level project:
<groupId>com.vps</groupId>
<artifactId>main-module</artifactId>
<version>5.0</version>
<packaging>pom</packaging>
There is another multi-module project that needs to be parented by this top-level project, and it has independent versioning from the top-level project.
The projects are not expected to be in the same repository, and are generally released independently of each other.
I want to declare a property that I can further use to refer to the version of this top-level project parent (say to pull in artifacts with the correct version):
<artifactId>auxiliary</artifactId>
<version>1.0</version>
<parent>
<groupId>com.vps</groupId>
<artifactId>main-module</artifactId>
<version>5.0</version>
</parent>
<properties>
<main.version>${project.parent.version}</main.version>
</properties>
<modules><module>corelib</module></modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vps</groupId>
<artifactId>main-module-1</artifactId>
<version>${main.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
Now, the sub-module is to pull and use the dependency:
<parent>
<groupId>com.vps</groupId>
<artifactId>auxiliary</artifactId>
<version>1.0</version>
</parent>
<artifactId>corelib</artifactId>
<properties>
<!-- this doesn't work
<main.version>${project.parent.parent.version}</main.version>
-->
</properties>
<dependencies>
<dependency>
<groupId>com.vps</groupId>
<artifactId>main-module-1</artifactId>
</dependency>
</dependencies>
If I set main.version as a reference to ${project.parent.version} in the auxiliary POM, compilation of corelib fails because it tries to pull com.vps:main-module-1:1.0, which doesn't exist. I guess this is because the properties are resolved based on the effective POM being processed at the moment, and from there, the ${project.parent.version} is the version of corelib's parent.
If I override main.version in the corelib sub-module as ${project.parent.parent.version}, I get an error saying that effective version computed for com.vps:main-module-1 is '${project.parent.parent.version}', and is invalid. I guess this means that the property cannot be resolved all together, but I can't quite understand why.
So, how do I reasonably (i.e. without hardcoding the top-level in both the parent definition and another property) refer to that top-level version value from descendant submodules?

Maven POM packaging with dependencies

I'm trying to construct a codebase where subsystems can be developed as maven modules, without the importing POM needing to concern itself with the internal structure of the maven module.
The "importing" pom
<project>
<artifactId>application</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<artifactId>submodule-1</artifactId>
</dependency>
</dependencies>
</project>
The "imported" pom
<project>
<artifactId>submodule-1</artifactId>
<packaging>pom</packaging>
<modules>
<module>api</module>
<module>implementation</module>
</modules>
<dependencies>
<dependency>
<artifactId>api</artifactId>
</dependency>
<dependency>
<artifactId>implementation</artifactId>
</dependency>
</dependencies>
</project>
This does seem to work, at least partially; the generated JARs appear to be on the classpath during mvn package. IntelliJ shows the application has a dependency on submodule-1 and transitively on api and implementation. However, mvn dependency:tree fails while building submodule-1 saying
Could not resolve dependencies for project submodule-1:pom:1.0.0-SNAPSHOT: Could not find artifact api:jar:1.0.0-SNAPSHOT
I'm trying to determine if this is a valid pattern, that of having packaging of pom, with defined dependencies which are also defined modules in the POM.
Have I stumbled upon a working-but-not-supported edge case, or is the dependency plugin broken in some way, or I'm breaking it in some way, or something else?

Avoid wrong version interpolation if child's pom version is different from those of the parent's aggregator pom and its sub modules

Problem description
We have a Maven aggregator pom with some child poms (modules) all having the same version:
pom.xml (parent zoo, version 2.0.0)
|-- pom.xml (child module cat, version 2.0.0)
|-- pom.xml (child module dog, version 2.0.0)
|-- ...
Within the dependency management section all children are declared with the project version to ease declaration of dependencies.
The parent pom looks like
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
<packaging>pom</packaging>
<modules>
<module>cat</module>
<module>dog</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<version>${project.version}</version>
</dependency>
<!-- other child modules go here -->
</dependencies>
</dependencyManagement>
The child poms are defined as
<parent>
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
</parent>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>dog</artifactId>
</dependency>
</dependencies>
There is another pom which declares the parent pom as its parent too (inheritance) but is not listed as sub module in this parent (no aggregation). This pom has a different version.
<parent>
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
</parent>
<groupId>com.acme</groupId>
<artifactId>boo</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>dog</artifactId>
</dependency>
</dependencies>
Actually we have expected that the version of the dependency com.acme.dog is pulled from the dependency management section of the parent pom com.acme.zoo and is equal to 2.0.0. However the Maven documentation on project interpolation and variables says
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.
That is: in the reactor build the variable ${project.version} used in the dependency management section of the parent pom com.acme.zoo is evaluated with respect to com.acme.bar and equal to 1.0.0 what is not as intended.
Note
There is a workaround with using a variable in the parent pom which has to be kept in sync with the parent pom versions. However, this solution is incompatible with the Maven Release Plugin.
Question
How can we achieve the desired behaviour
aggregator pom with children having the same version
declaration of children in the dependency management section to ensure that all dependencies have the same version
use of inheritance together with different versions
compatibility with maven-release-plugin
without the pitfalls of project interpolation of variables?
The maven release plugin is able to change the versions of the dependencies managed in the parent pom.
So if you define your maven parent like this:
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>cat</module>
<module>dog</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<!-- other child modules go here -->
</dependencies>
</dependencyManagement>
As you see the versions of the parent and the managed dependency are the same. I set them to a SNAPSHOT version because the release plugin will create the final versions on release:perform
Your child poms can stay as you had them.
Because in your setup, your parent project is also the reactor you can then call
mvn release:perform -DautoVersionSubmodules=true
which will update the version of the parent in all submodules when you run this command. That option is essentially the same as if you run
mvn versions:update-child-modules
meaning it will change the child poms.
After you run the mvn release:perform command your parent pom will look like this:
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>cat</module>
<module>dog</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<version>2.0.1-SNAPSHOT</version>
</dependency>
<!-- other child modules go here -->
</dependencies>
</dependencyManagement>
and your child poms like this
<parent>
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.1-SNAPSHOT</version>
</parent>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>dog</artifactId>
</dependency>
</dependencies>
The final versions will only exist in the tag created by the release:prepare command.
PS: You may define other versions for the final and the next development version when they are prompted after running the release:prepare command.
The simplest solution is modify pom of zoo and replace <version>${project.version}</version> with <version>2.0.0</version>
Please note:
when you change version to next number, for example 2.0.1, with
versions-maven-plugin, dependency management section will be also
updated.
Spring use simplest solution, see
http://central.maven.org/maven2/org/springframework/spring-framework-bom/4.2.7.RELEASE/spring-framework-bom-4.2.7.RELEASE.pom
Summary: using <version>${project.version}</version> in dependency management is wrong idea.
From Maven Introduction to the pom : http://maven.apache.org/guides/introduction/introduction-to-the-pom.html
Project Inheritance > Example 1 > The Solution
Alternatively, if we want the groupId and / or the version of your
modules to be the same as their parents, you can remove the groupId
and / or the version identity of your module in its POM.
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>
My approach to that is to track it in the child POM. It's a bit less typing overall, close to where the actual dependency lives and is low maintenance for most projects. YMMV
<dependencies>
...
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>foo-sibling</artifactId>
<version>${project.version}</version>
</dependency>
...
</dependencies>

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

Dependeny Management using POM import

I am creating a project 'test-jar' in my local and i am using pom file which I don't have write access as Parent of 'test-jar' project. The parent project has already defined depedencyManagement with old versions.
As I have to update dependency versions in my project and planning to override parent's dependency Management. So, I have created another POM file with my own dependency Management and imported into 'test-jar' project.
My Project :
<project>
<artifactid>test-jar</artifactid>
<parent>
<artifactId> test-parent </artifactId>
</parent>
<dependencies>
<dependency>
<artifactId>jar/artifactId>
</dependency>
<dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>custom-pom</artifactId>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
</project>
My Parent Project:
<project>
<artifactid>test-parent</artifactid>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>jar/artifactId>
<version>1.0</version>
</dependency>
</dependencyManagement>
</project>
My Custom POM for updated dependencyManagement:
<project>
<artifactid>custom-pom</artifactid>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>jar</artifactId>
<version>3.0</version>
</dependency>
</dependencyManagement>
</project>
The problem is, I am always getting dependency version from parent pom, though i have imported new dependency management in project.
I am using Maven 2.2.1 version here.
Is there any solution how to overwrite Dependency Management from Parent POM ?
Based on the documentation:
This scope is only used on a dependency of type pom in the
section. It indicates that the specified POM
should be replaced with the dependencies in that POM's
section. Since they are replaced, dependencies
with a scope of import do not actually participate in limiting the
transitivity of a dependency.
Apart from your problem you can simply use differerent version which are different of the onses defined in the parent pom's dependencyManagement. Furthermore you could create a separate pom wich defines the dependencies with new version (dependencyManagement) and inherits from the given parent.

Resources