Version artifact (Maven)

Introduction

There are many ways to generate a version file with Maven to be used as a reference of the artifact when it has been deployed (at runtime).

This article just details one way to achieve it (it's not been, at all, the goal to explain nor the best way not the most recommended one).

Reference

Get your application version with Spring Boot

https://blog.jdriven.com/2018/10/get-your-application-version-with-spring-boot/


maven-antrun-plugin

This plugin generates a properties file that, later, will be able to check the artifact version and compilation instant.

This configuration appears in pom.xml; at the build > plugins section:

    <plugin>

    <!-- It generates a 'public/version.properties' file > http://localhost:8080/version -->

    <groupId>org.apache.maven.plugins</groupId>

    <artifactId>maven-antrun-plugin</artifactId>

    <!-- <version>${maven-antrun-plugin.version}</version> -->

    <executions>

     <execution>

      <phase>process-resources</phase>

      <goals>

       <goal>run</goal>

      </goals>

      <configuration>

       <target>

        <tstamp>

         <format property="last.updated"

          pattern="yyyy-MM-dd'T'HH:mm:ssZ" timezone="Z" />

        </tstamp>

        <propertyfile

         file="${basedir}/target/classes/public/version.properties"

         comment="Compilation information (generated)">

         <entry key="build.time" value="${last.updated}" />

         <entry key="project.groupId" value="${project.groupId}" />

         <entry key="project.artifactId"

          value="${project.artifactId}" />

         <entry key="project.version" value="${project.version}" />

        </propertyfile>

       </target>

      </configuration>

     </execution>

    </executions>

   </plugin>

compilation.properties

This properties file is automatically generated by the maven-antrun-plugin.

Note that file is located in a 'public' location, and therefore be accessed e.g: http://localhost:8080/public

#Compilation information (generated, do not edit)

#Thu, 02 Nov 2017 17:30:38 +0100

build.time=2017-11-02T16\:30\:38+0000

project.groupId=edu.cou

project.artifactId=f6

project.version=1.4.0-SNAPSHOT

version.jsp

This little program allows, at runtime, check which version of the artifact is being executed.

It is located at the src/main/webapp/ directory:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.util.Properties"%>

<%@ page import="java.io.InputStream"%>

<%@ page import="java.io.IOException"%>

<%

    String build = "?";

    String groupId = "?";

    String artifactId = "?";   

    String version = "?";   

    String ipClient=request.getRemoteAddr();

   

    Properties properties = new Properties();

    try {

        ClassLoader cl = this.getClass().getClassLoader();

        InputStream is = cl.getResourceAsStream("compilacio.properties");

        properties.load(is);

       

        build =  properties.getProperty("build.time");

        groupId = properties.getProperty("project.groupId");

        artifactId = properties.getProperty("project.artifactId");

        version = properties.getProperty("project.version");

    } catch (IOException e) {

        e.printStackTrace();

    }

%>

<!DOCTYPE html>

<html>

<head>

  <meta http-equiv="content-type" content="text/html; charset=UTF-8">

  <title>Versió</title>

  <!-- CSS normalization - http://jonathantneal.github.io/sanitize.css/ -->

  <link rel = "stylesheet" type = "text/css" href = "https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/5.0.0/sanitize.min.css" />

</head>

<body>

  <h2>F6</h2>

  <table>

    <tr><td>Compilat:</td><td><%=build%></td></tr>

    <tr><td>IP Client:</td><td><%=ipClient%></td></tr>

    <tr><td>&nbsp;</td><td>&nbsp;</td></tr>

    <tr><td>project.groupId:</td><td><%=groupId%></td></tr>

    <tr><td>project.artifactId:</td><td><%=artifactId%></td></tr>

    <tr><td>project.version:</td><td><%=version%></td></tr>

  </table>

</body>

</html>

Output

At runtime, the version file can be checked via URL

/f6/version.jsp

A sample output follows:

F6

Compilat: 2017-10-27T11:31:07+0000

IP Client: 192.168.111.111

project.groupId: edu.cou

project.artifactId: f6

project.version: 1.4.0-SNAPSHOT


Spring Boot, no Spring, application.properties version from Maven POM

Using Maven property replacement (version)

Maven property expansion works out of the box if using in POM the parent spring-boot-starter-parent.

But only for the 'main' application*.properties (the solution below makes it work also for the 'test' ones).


src/main/resources/application.properties

#-------------------------------------------------------------

# 0. App custom properties

#-------------------------------------------------------------

app.version=@project.version@


src/test/resources/application.properties

#-------------------------------------------------------------

# 0. App custom properties (workaround default values for tests)

#-------------------------------------------------------------

app.version=@project.version@


Sample Java reading the property (eg: in class attribute or method parameter)

@Value("${app.version:unknown}") String appVersion


pom.xml

  <build>

    <!-- Spring Boot Maven property expansion also for test -->

    <testResources>

      <testResource>

        <filtering>true</filtering>

        <directory>src/test/resources</directory>

        <includes>

          <include>**/application*.properties</include>

        </includes>

      </testResource>

    </testResources>

    <finalName>myapp_mytechcomp</finalName>


spring.config.location

If you specify the application.properties file to use in the spring-boot:run command line then ensure you use the 'replaced' one in 'target' (not the original one in 'src'), eg:

./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location=file:./target/test-classes/

instead of:

./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.config.location=file:./src/test/resources/



Spring, no Spring Boot, legacy webservice (version from Maven pom.properties)

Using Spring Resource (version)

Warning: This option might not work w/ Jetty but it works w/ JBoss.

  public String getVersion() throws WSException {


/* (this should be the right one but...) */

final String POM_PROPERTIES_LOCATION_CLASSPATH =

"classpath:META-INF/maven/edu.cou.services.cat.evaluation-ws/evaluation-service/pom.properties";


/* (should also work but...) */

final String POM_PROPERTIES_LOCATION_RELATIVE =

"META-INF/maven/edu.cou.services.cat.evaluation-ws/evaluation-service/pom.properties";


/* (fallback w/ slash) */

final String POM_PROPERTIES_LOCATION_CLASSPATH =

"/META-INF/maven/edu.cou.services.cat.evaluation-ws/evaluation-service/pom.properties";


    final String artifactCodeFilename = POM_PROPERTIES_CL;


    Resource propsResource = this.context.getResource(POM_PROPERTIES_LOCATION_CLASSPATH);

    if (!propsResource.exists()) {

      propsResource = this.context.getResource(POM_PROPERTIES_LOCATION_RELATIVE);

    }

    if (!propsResource.exists()) {

      propsResource = this.context.getResource(artifactCodeFilename);

    }


    InputStream propertiesIs = null;

    try {

      if (propsResource.exists()) {

        propertiesIs = propsResource.getInputStream();

      } else {

        throw new NoSuchElementException("Resource doesn't exist " + artifactCodeFilename);

      }


      if (propertiesIs == null) {

        throw new NoSuchElementException("No input stream for resource " + artifactCodeFilename);

      }

      Properties prop = new Properties();

      prop.load(propertiesIs);


      return prop.getProperty("version");

    } catch (NoSuchElementException e) {

      throw new WSException(e.getMessage(), e);

    } catch (Exception e) {

      throw new WSException("Error obtaining artifact version from " + artifactCodeFilename, e);


    } finally {

      if (propertiesIs != null) {

        try {

          propertiesIs.close();

        } catch (IOException e) {

          logger.error(e);

        }

      }

    }

  }

Using Class Loader (version)

Warning: This option might not work in some environments.

  /** pom.properties (using Class Loader)*/

  private static final String POM_PROPERTIES_CL = //

      "/META-INF/maven/edu.myapp-ws/myapp-service/pom.properties";

...

  public String getVersionToBeDeleted() throws WSException {

    final String artifactCodeFilename = POM_PROPERTIES_CL;


    InputStream propertiesIs = null;

    try {

      propertiesIs = getClass().getClassLoader().getResourceAsStream(artifactCodeFilename);


      if (propertiesIs == null) {

        throw new NoSuchElementException("No input stream for resource " + artifactCodeFilename);

      }

      Properties prop = new Properties();

      prop.load(propertiesIs);


      return prop.getProperty("version");

    } catch (NoSuchElementException e) {

      throw new WSException(e.getMessage(), e);

    } catch (Exception e) {

      throw new WSException("Error obtaining artifact version from " + artifactCodeFilename, e);


    } finally {

      if (propertiesIs != null) {

        try {

          propertiesIs.close();

        } catch (IOException e) {

          LOGGER.error(e);

        }

      }

    }

  }