Version Informations Into Your Apps With Maven

It often happens having an application and you want to show a version information in a kind of About Dialog or may be on command line as well (probably you know --version stuff).

So in relationship with Maven the question is: How to get this into your Java code.

There are in general four ways to do it:

  1. Using the pom.properties file which is created by default by Maven.

  2. Using the information which is provided by the MANIFST.MF file. There are several ways to get those information.

  3. Create a property which is filtered during the build process and will be read by your application.

  4. Using a generated class which contains the appropriate informations.

#Using the pom.properties#

In every Maven build a property pom.properties is packaged into resulting archive (jar, ear, war, ejb etc.) which can be found under the following location:

1META-INF/maven/${groupId}/${artifactId}/pom.properties

This file contains the following entries

1version=${project.version}
2groupId=${project.groupId}
3artifactId=${project.artifactId}

They can be extracted by Java code like the following:

 1public class TheVersionClass
 2{
 3    ..
 4
 5    public TheVersionClass()
 6    {
 7        InputStream resourceAsStream =
 8          this.getClass().getResourceAsStream( 
 9            "/META-INF/maven/com.soebes.examples/version-examples-i/pom.properties" 
10          );
11        this.prop = new Properties();
12        try
13        {
14            this.prop.load( resourceAsStream );
15        }
16        ...
17    }
18}

The disadvantage of this approach is that you can not simply enhance the entries which are stored in the pom.properties file. For such a purpose you need to define your own pom.properties file.

#The MANIFEST.MF File#

In Java there exists a possibility to extract information from the MANIFEST.MF by using simple code lines like the following:

 1public class TheVersionClass {
 2    public TheVersionClass() {
 3        System.out.println( "  Implementation Title:" + this.getClass().getPackage().getImplementationTitle() );
 4        System.out.println( " Implementation Vendor:" + this.getClass().getPackage().getImplementationVendor() );
 5        System.out.println( "Implementation Version:" + this.getClass().getPackage().getImplementationVersion() );
 6        System.out.println( "    Specification Tile:" + this.getClass().getPackage().getSpecificationTitle() );
 7        System.out.println( "  Specification Vendor:" + this.getClass().getPackage().getSpecificationVendor() );
 8        System.out.println( " Specification Version:" + this.getClass().getPackage().getSpecificationVersion() );
 9    }
10}

But unfortunately within a Maven build this will result usually in null output, cause by default these information are not written to the MANIFEST.MF file. This can simply be fixed by using the following pom snippet:

 1<build>
 2  <pluginManagement>
 3    <plugins>
 4      <plugin>
 5        <artifactId>maven-jar-plugin</artifactId>
 6        <configuration>
 7          <archive>
 8            <manifest>
 9              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
10              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
11            </manifest>
12          </archive>
13        </configuration>
14      </plugin>
15    </plugins>
16  </pluginManagement>
17</build>

The main disadvantage of this approach is that you can only use the Implementation and Specification parts of your MANIFEST.MF file which might be enough in some situations but not in all.

#The Filtered Property File#

Apart from the above methods you can of course define a simple version.properties file within src/main/resources with any contens you like:

1version=${project.version}
2groupId=${project.groupId}
3artifactId=${project.artifactId}

You need to setup filtering in your project like this:

 1<build>
 2  <resources>
 3    <resource>
 4      <directory>src/main/resources</directory>
 5      <includes>
 6        <include>version.properties</include>
 7      </includes>
 8      <filtering>true</filtering>
 9    </resource>
10  </resources>
11</build>

The entries can be extracted by Java code like the following which is more or less exactly the same as before (except for the path):

 1public class TheVersionClass
 2{
 3    ..
 4
 5    public TheVersionClass()
 6    {
 7        InputStream resourceAsStream =
 8          this.getClass().getResourceAsStream( 
 9            "/version.properties" 
10          );
11        this.prop = new Properties();
12        try
13        {
14            this.prop.load( resourceAsStream );
15        }
16        ...
17    }
18}

#The Generated Class#

You can use the templating-maven-plugin which can be used to create a class which contains the appropriate informations.

The first step is to create a template file within the following location src/main/java-templates like the following:

 1public final class Version {
 2
 3    private static final String VERSION = "${project.version}";
 4    private static final String GROUPID = "${project.groupId}";
 5    private static final String ARTIFACTID = "${project.artifactId}";
 6    private static final String REVISION = "${buildNumber}";
 7
 8    public static String getVersion() {
 9        return VERSION;
10    }
11    // other getters...
12}

This class can contain any reference to properties or project related information which you can access via the ${project...} etc.

The ${buildNumber} is an example for using other placeholders which in this case is filled by the buildnumber-maven-plugin which usually provides the SVN/Git revision number.

The following pom snippet is used to generate the Java source from the Java template file and make sure it will be compiled by the maven-compiler-plugin.

 1<plugins>
 2  <plugin>
 3    <groupId>org.codehaus.mojo</groupId>
 4    <artifactId>templating-maven-plugin</artifactId>
 5    <version>1.0.0</version>
 6    <executions>
 7      <execution>
 8        <id>generate-verion-class</id>
 9        <goals>
10          <goal>filter-sources</goal>
11        </goals>
12      </execution>
13    </executions>
14  </plugin>
15</plugins>

So based on the given information you see there are several ways to get the problem solved. It depends you which you decide to use. All the above examples can be found on github repository with running integration tests to show the results.