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> </td><td> </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);
}
}
}
}