2

I have a Java 11 application which I develop using Maven and in the pom.xml I have a version declared.

<groupId>my.group.id</groupId>
<artifactId>artifact</artifactId>
<version>0.1.2.3</version>

I want to get this version at runtime e.g. using getClass().getPackage().getImplementationVersion() as it's described in this question. This works as long as I don't package my application as a modular runtime image using Jlink. Then I only get null returned from above call.

I package my application using:

jlink --output target/artifact-image --module-path target/dependencies --launcher MyApp=my.module.name/my.main.Class --add-modules my.module.name

Jlink has actually a parameter --version but this returns the Jlink version instead setting it for the generated artifact.

So, how can I get the version (of my Maven project) at runtime?

  • How to define it in the modular application?
  • How to get it into the modular application?
  • How to read it in the modular application?

I know I could define it in a resource file and simply read it from there, however I prefer to have it only in the pom.xml (= to have a single source of truth).

Itchy
  • 2,263
  • 28
  • 41

2 Answers2

0

In the end I did this using the filtering function of the Maven Resources Plugin.

First, enable filtering in the pom.xml:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

Then add a src/main/resources/my-version.properties file containig:

my.version=${project.version}

So you can use the following code in Java:

Properties myProperties = new Properties();
try {
    myProperties.load(getClass().getResourceAsStream("/my-version.properties"));
} catch (IOException e) {
    throw new IllegalStateException(e);
}
String theVersion = Objects.requireNonNull((String) myProperties.get("my.version"));
Itchy
  • 2,263
  • 28
  • 41
0

I had a similar problem in my last job. I needed to get the version for modules/jars that are not a direct dependency of the application, as well as the module's version itself. The classpath is assembled from multiple modules when the application starts, the main application module has no knowledge of how many jars are added later.

That's why I came up with a different solution, which may be a little more elegant than having to read XML or properties from jar files.

The idea

  1. use a Java service loader approach to be able to add as many components/artifacts later, which can contribute their own versions at runtime. Create a very lightweight library with just a few lines of code to read, find, filter and sort all of the artifact versions on the classpath.
  2. Create a maven source code generator plugin that generates the service implementation for each of the modules at compile time, package a very simple service in each of the jars.

The solution

Part one of the solution is the artifact-version-service library, which can be found on github and MavenCentral now. It covers the service definition and a few ways to get the artifact versions at runtime.

Part two is the artifact-version-maven-plugin, which can also be found on github and MavenCentral. It is used to have a hassle-free generator implementing the service definition for each of the artifacts.

Examples

Fetching all modules with coordinates

No more reading jar manifests or property files, just a simple method call:

// iterate list of artifact dependencies
for (Artifact artifact : ArtifactVersionCollector.collectArtifacts()) {
    // print simple artifact string example
    System.out.println("artifact = " + artifact);
}

A sorted set of artifacts is returned. To modify the sorting order, provide a custom comparator:

new ArtifactVersionCollector(Comparator.comparing(Artifact::getVersion)).collect();

This way the list of artifacts is returned sorted by version numbers.

Find a specific artifact

ArtifactVersionCollector.findArtifact("de.westemeyer", "artifact-version-service");

Fetches the version details for a specific artifact.

Find artifacts with matching groupId(s)

Find all artifacts with groupId de.westemeyer (exact match):

ArtifactVersionCollector.findArtifactsByGroupId("de.westemeyer", true);

Find all artifacts where groupId starts with de.westemeyer:

ArtifactVersionCollector.findArtifactsByGroupId("de.westemeyer", false);

Sort result by version number:

new ArtifactVersionCollector(Comparator.comparing(Artifact::getVersion)).artifactsByGroupId("de.", false);

Implement custom actions on list of artifacts

By supplying a lambda, the very first example could be implemented like this:

ArtifactVersionCollector.iterateArtifacts(a -> {
    System.out.println(a);
    return false;
});

Installation

Add these two tags to all pom.xml files, or maybe to a company master pom somewhere:

<build>
  <plugins>
    <plugin>
      <groupId>de.westemeyer</groupId>
      <artifactId>artifact-version-maven-plugin</artifactId>
      <version>1.1.1</version>
      <executions>
        <execution>
          <goals>
            <goal>generate-service</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

<dependencies>
  <dependency>
    <groupId>de.westemeyer</groupId>
    <artifactId>artifact-version-service</artifactId>
    <version>1.1.1</version>
  </dependency>
</dependencies>

Feedback

It would be great if you could give the solution a try. Getting feedback about whether you think the solution fits your needs would be even better. So please don't hesitate to add a new issue on any of the github projects if you have any suggestions, feature requests, problems, whatsoever.

Licence

All of the source code is open source, free to use even for commercial products (MIT licence).