Skip to main content
  1. Posts/

Use a unique property to set Maven version

·4 mins·

Published 9 years ago: the tools and commands described may have changed since.

When working with a Maven multi-module (dozens of them) project on multiple branches, I always end up with merge conflicts on my pom.xml <version> elements.
Here is a solution I use to remove all the <version> element duplication.

Our example project is made of two modules:

.
├── pom.xml
├── submodule-1
│   └── pom.xml
└── submodule-2
    └── pom.xml

Parent project pom.xml looks like:

<project>
    <groupId>com.github.jcgay.example.version</groupId>
    <artifactId>parent-pom</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>submodule-1</module>
        <module>submodule-2</module>
    </modules>
        
</project>

and the ones for modules:

<project>
    <parent>
        <groupId>com.github.jcgay.example.version</groupId>
        <artifactId>parent-pom</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>submodule-1</artifactId>

</project>

Project version is duplicated in each module’s pom.xml, it’s easy to change them with the Versions plugin. But merges between branches are a pain, conflicts are happening and we just can’t keep the changes from one of the branch 😓.

Define the version with a property
#

First try, extract the version is a Maven property. Our parent pom becomes:

<project>
    <groupId>com.github.jcgay.example.version</groupId>
    <artifactId>parent-pom</artifactId>
    <version>${my-version}</version>
    <packaging>pom</packaging>

    <properties>
        <my-version>1.0-SNAPSHOT</my-version>
    </properties>

    <modules>
        <module>submodule-1</module>
        <module>submodule-2</module>
    </modules>

</project>

and the module use the same ${my-version} property to designate its parent:

<project>
    <parent>
        <groupId>com.github.jcgay.example.version</groupId>
        <artifactId>parent-pom</artifactId>
        <version>${my-version}</version>
    </parent>

    <artifactId>submodule-2</artifactId>

</project>

Surprisingly it works 😲
Only downside is that warning messages are displayed by Maven.

Some problems were encountered while building the effective model for com.github.jcgay.example.version:submodule-1:jar:1.0-SNAPSHOT
'version' contains an expression but should be a constant. @ com.github.jcgay.example.version:parent-pom:${my-version}, /Users/jcgay/dev/toolbox-examples/unique-maven-version/pom.xml, line 7, column 14

Some problems were encountered while building the effective model for com.github.jcgay.example.version:submodule-2:jar:1.0-SNAPSHOT
'version' contains an expression but should be a constant. @ com.github.jcgay.example.version:parent-pom:${my-version}, /Users/jcgay/dev/toolbox-examples/unique-maven-version/pom.xml, line 7, column 14

Some problems were encountered while building the effective model for com.github.jcgay.example.version:parent-pom:pom:1.0-SNAPSHOT
'version' contains an expression but should be a constant. @ com.github.jcgay.example.version:parent-pom:${my-version}, /Users/jcgay/dev/toolbox-examples/unique-maven-version/pom.xml, line 7, column 14

It is highly recommended to fix these problems because they threaten the stability of your build.

For this reason, future Maven versions might no longer support building such malformed projects.

So we can’t just really use that, and there is also a much bigger problem but we will discover it later.

Continuous delivery friendly version
#

Since Maven 3.2.1 we can use registered properties to hide the previous warning messages.

Replacing the property my-version by revision will do the trick 🎆.

But the real problem is showing now… When Maven install (or deploy) ours artifacts, pom.xml are copied without modification (${revision} properties are not replaced by their resolved value). If you try to depend on one of these artifacts it will fail because ${revision} will not be set in this outside project.

Example with this simple project which is not built within parent-pom reactor:

<project>
    <groupId>com.github.jcgay.example.version</groupId>
    <artifactId>independant-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <dependencies>
        <dependency>
            <groupId>com.github.jcgay.example.version</groupId>
            <artifactId>submodule-1</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

When we launch a build:

$> mvn test

Scanning for projects...

Using the MultiThreadedBuilder implementation with a thread count of 4

------------------------------------------------------------------------
Building independant-project 1.0-SNAPSHOT
------------------------------------------------------------------------
Downloading: https://repo.maven.apache.org/maven2/com/github/jcgay/example/version/parent-pom/$%7Brevision%7D/parent-pom-$%7Brevision%7D.pom
------------------------------------------------------------------------
BUILD FAILURE
------------------------------------------------------------------------
Total time: 0.882 s (Wall Clock)
Finished at: 2016-08-09T15:26:09+02:00
Final Memory: 12M/193M
------------------------------------------------------------------------
Failed to execute goal on project independant-project: Could not resolve dependencies for project com.github.jcgay.example.version:independant-project:jar:1.0-SNAPSHOT: Failed to collect dependencies at com.github.jcgay.example.version:submodule-1:jar:1.0-SNAPSHOT: Failed to read artifact descriptor for com.github.jcgay.example.version:submodule-1:jar:1.0-SNAPSHOT: Could not find artifact com.github.jcgay.example.version:parent-pom:pom:${revision} in central (https://repo.maven.apache.org/maven2) -> [Help 1]

In fact the pom.xml available in the local repository still defines ${revision} as a <version> for the pom-parent.

Filtering pom at installation/deployment
#

These non filtered properties remind me an old blog post. The author was filtering (with maven-resources-plugin) its pom.xml to install them instead of the originals. I was wondering if this could be achieved by a Maven extension (the amount of necessary XML and the hacky feeling of the solution just discourage me…). This is doable with Aether (used by Maven 3) by implementing a org.eclipse.aether.impl.MetadataGenerator. We can code our hack 🤔.

Code is available at GitHub. This a Maven extension which will transform pom artifacts when installing and deploying. Each ${revision} occurrence is replaced by the artifact’s resolved version. Problem solved!

Activate it using the core extensions configuration mechanism by creating a ${maven.multiModuleProjectDirectory}/.mvn/extensions.xml file with:

<?xml version="1.0" encoding="UTF-8"?>
<extensions>
    <extension>
      <groupId>fr.jcgay.maven.extension</groupId>
      <artifactId>unique-revision-maven-filtering</artifactId>
      <version>[latest-version]</version>
    </extension>
</extensions>

or by copying jar to %M2_HOME%/lib/ext.

Conclusion
#

We can now use a unique property to set version across all modules in a multi-modules project. This does not seems to play by the rules of the continuous delivery friendly version but merges can now be done without resolving multiple conflicts on <version> XML elements!
I may have totally missed a simpler way to do this ? (in that case I’d really like to know it 😉).

Share

Related


Archived comments (7)

The original discussion, kept as it was. It is closed.

MHelena Chaves

Hi Jean! My name is Maria Helena and I work as a configuration analyst here in Brazil (Campinas - São Paulo). This issue with the Maven version configured in all pom files of the source code was really boring, specially when we are doing merges between branches (and we do it a lot, believe me!). I have just configure our code to use your solution and it worked perfectly! I would like to thank very much!! Best regards!

Jean-Christophe GAY

Nice to read :) Thanks !

I don't know if you have explored other ways but some have developed a custom git merge driver [https://github.com/ralfth/p...] to solve this too.

MHelena Chaves

Yes, we've tried do use it with GIT, but it doesn't work in the same way as when we use "find . -name pom.xml | xargs svn resolve --accept mine-conflict" when working with Subversion. So, when working with GIT we used to change all versions of one of the sides do avoid conflicts (for example: 'find . -type f -name pom.xml -print | xargs perl -pi -e 's@>10.19.0-AUT-SNAPSHOT<@>10.20.0-SNAPSHOT<@g').

hazriqpedia

Hey man. Thanks for this. Still useful in 2017 :D

Jean-Christophe GAY

Yeah it does a lot more than just replacing properties but I didn't know it at the time :p

There is also a really great article on how to use it at http://blog.soebes.de/blog/...

Dave Neeley

Good notes there too. Thanks!