Retrieve version from maven pom.xml in code
Assuming you're using Java, you can:
Create a
.properties
file in (most commonly) yoursrc/main/resources
directory (but in step 4 you could tell it to look elsewhere).Set the value of some property in your
.properties
file using the standard Maven property for project version:foo.bar=${project.version}
In your Java code, load the value from the properties file as a resource from the classpath (google for copious examples of how to do this, but here's an example for starters).
In Maven, enable resource filtering. This will cause Maven to copy that file into your output classes and translate the resource during that copy, interpreting the property. You can find some info here but you mostly just do this in your pom:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
You can also get to other standard properties like project.name
, project.description
, or even arbitrary properties you put in your pom <properties>
, etc. Resource filtering, combined with Maven profiles, can give you variable build behavior at build time. When you specify a profile at runtime with -PmyProfile
, that can enable properties that then can show up in your build.
Get version of project from pom.xml
I'll explain it backwards:
- Show value in a facelet (not requested by OP)
- The managed bean serving the values
- pom.xml to create manifest.mf
So let's start to show the revision number:
<h:outputText value="#{appInfo.revision}" />
The result looks like this, where 0.0.40
is the version
out of the poms project version:
0.0.40 / 29.05.2018 12:42
As you see the (as shown below) the result shows the revision and the build date. Be tolerant, the following code uses version
for the build date.
appInfo
is an ApplicationScoped bean and fetches the version/revision info from manifest. Here are the relevant parts of the bean:
@Named
@ApplicationScoped
public class AppInfo {
private String revision; // + getter
@PostConstruct
public void init() {
InputStream is = FacesContext.getCurrentInstance().getExternalContext()
.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest();
try {
manifest.read(is);
} catch (IOException e) {
// error handling
} finally {
try {
is.close();
} catch (IOException e) {
// error handling
}
}
Attributes attr = manifest.getMainAttributes();
String implRevision = attr.getValue("Implementation-Build");
String implVersion = attr.getValue("Implementation-Version");
if (implRevision == null || implVersion == null)
this.revision = "unknown";
else
this.revision = implVersion + " / " + implRevision;
}
}
The buildnumber is fetched from pom.xml by maven build number plugin (http://www.mojohaus.org/buildnumber-maven-plugin/) and the manifest is generated by maven war plugin.
In the build part of your pom you
- put the revision info inside a variable (called
${buildNumber}
) - use this value to generate manifest
Snippets from pom.xml:
<project>
<build>
<plugins>
<!-- revision number to ${buildNumber} -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<format>{0,date,dd.MM.yyyy HH:mm}</format>
<items>
<item>timestamp</item>
</items>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
</configuration>
</plugin>
<!-- END: revision number to ${buildNumber} -->
<!-- START: generate MANIFEST.MF -->
<plugin>
<artifactId>maven-war-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>2.5</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<version>${project.version}</version>
<Implementation-Build>${buildNumber}</Implementation-Build>
</manifestEntries>
</archive>
</configuration>
</plugin>
<!-- END: generate MANIFEST.MF -->
</plugins>
</build>
</project>
Because I have created this a while ago some parts may be (a little bit) outdated, buth they still work fine.
Get the version from pom.xml from a Java Maven project in Bitbucket pipeline
Based on the tip of @joe and the similar question How to get Maven project version to the bash command line, I can confirm this works on Bitbucket pipelines:
- step:
name: Get the version from pom.xml
script:
- MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
- echo $MVN_VERSION
Result
+ MVN_VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
+ echo $MVN_VERSION
0.0.1-SNAPSHOT
How to log the pom artifact and version
Solution A: Maven resource filtering
Your POM snippet should replace the variables correctly, if you put it in the right POM section:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
You can inspect the result in the target/classes
folder. After I fixed your faulty pseudo code by adding an empty argument list ()
to your method name and replaced the nonsensical this.getClassLoader()
by getClass().getClassLoader()
, the code even compiles and does something meaningful. Do you ever test before you post something to a public platform like StackOverflow?
import java.io.IOException;
import java.util.Properties;
public class ClassA {
public void methodA() throws IOException {
final Properties properties = new Properties();
properties.load(getClass().getClassLoader().getResourceAsStream("project.properties"));
System.out.println(properties.getProperty("version"));
System.out.println(properties.getProperty("artifactId"));
}
public static void main(String[] args) throws IOException {
new ClassA().methodA();
}
}
Console log when running from IntelliJ IDEA after mvn compile
(because we need Maven to process the resources and copy them to target/classes
):
1.9.8-SNAPSHOT
util
Or whatever your module name and version are.
If you see the variable names instead, either your classpath does not point to the JAR but to the source directory somehow, or you have multiple modules with a project.properties
file and in one of them forgot resource filtering. Whichever file is found first on the class path, will be loaded. So in a multi-module project, you better use different file names, otherwise it is more or less a lottery which one if found first.
The next problem would then be for your aspect or other module to know which resource file to load, so that better be linked to class or package names somehow in order for the other module to be able to guess the resource file from the package name. You do need clean package name separation between modules then. I really wonder if it is worth the trouble.
Solution B: Templating Maven Plugin + package-info.java
+ custom annotation
Another idea would be to use resource filtering or a plugin like org.codehaus.mojo:templating-maven-plugin
for replacing the versions directly into package annotation values in a package-info.java
file and then simply fetch the values during runtime from the package info. I made a quick & dirty local test with that plugin, and it works nicely. I recommend to keep it simple for now and just fix your resource filtering problem. If you need the more generic solution I just described, please let me know.
Project structure
Update: I extracted the quick solution I hacked into one of my projects into a new Maven multi-module project in order to show you a clean solution as follows:
Say, we have a parent POM with 3 sub-modules:
annotation
- contains an annotation to be used on packages inpackage-info.java
files. Can easily be modified to also be applicable to classes.library
- example library to be accessed by an application moduleapplication
- example application
You can find the full project on GitHub:
https://github.com/kriegaex/SO_Maven_ArtifactInfoRuntime_68321439
The project's directory layout is as follows:
$ tree
.
├── annotation
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── de
│ └── scrum_master
│ └── stackoverflow
│ └── q68321439
│ └── annotation
│ └── MavenModuleInfo.java
├── application
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── de
│ │ │ └── scrum_master
│ │ │ └── stackoverflow
│ │ │ └── q68321439
│ │ │ └── application
│ │ │ └── Application.java
│ │ └── java-templates
│ │ └── de
│ │ └── scrum_master
│ │ └── stackoverflow
│ │ └── q68321439
│ │ └── application
│ │ └── package-info.java
│ └── test
│ └── java
│ └── de
│ └── scrum_master
│ └── stackoverflow
│ └── q68321439
│ └── application
│ └── ModuleInfoTest.java
├── library
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── de
│ │ └── scrum_master
│ │ └── stackoverflow
│ │ └── q68321439
│ │ └── library
│ │ └── LibraryClass.java
│ └── java-templates
│ └── de
│ └── scrum_master
│ └── stackoverflow
│ └── q68321439
│ └── library
│ └── package-info.java
└── pom.xml
Please note the src/java-templates
directories in both the library and the application modules, containing package-info.java
files. The directory name is the default for Templating Maven Plugin, making plugin configuration less verbose.
Parent POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<modules>
<module>annotation</module>
<module>library</module>
<module>application</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>filter-src</id>
<goals>
<goal>filter-sources</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Module annotation
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>annotation</artifactId>
</project>
package de.scrum_master.stackoverflow.q68321439.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MavenModuleInfo {
String groupId();
String artifactId();
String version();
}
Module library
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>library</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>annotation</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
package de.scrum_master.stackoverflow.q68321439.library;
public class LibraryClass {}
Please note that the following file needs to be located in library/src/main/java-templates/de/scrum_master/stackoverflow/q68321439/library/package-info.java
. Here you can see how we use Maven properties to be replaced by their corresponding values during the build process by Templating Maven Plugin:
/**
* This is the package description (...)
*/
@MavenModuleInfo(groupId = "${project.groupId}", artifactId = "${project.artifactId}", version = "${project.version}")
package de.scrum_master.stackoverflow.q68321439.library;
import de.scrum_master.stackoverflow.q68321439.annotation.MavenModuleInfo;
Module application
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>maven-artifact-info-runtime</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>application</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>annotation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>library</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>de.scrum-master.stackoverflow.q68321439</groupId>
<artifactId>library</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package de.scrum_master.stackoverflow.q68321439.application;
public class Application {
public static void main(String[] args) {}
}
Please note that the following file needs to be located in application/src/main/java-templates/de/scrum_master/stackoverflow/q68321439/application/package-info.java
. Here you can see how we use Maven properties to be replaced by their corresponding values during the build process by Templating Maven Plugin:
/**
* This is the package description (...)
*/
@MavenModuleInfo(groupId = "${project.groupId}", artifactId = "${project.artifactId}", version = "${project.version}")
package de.scrum_master.stackoverflow.q68321439.application;
import de.scrum_master.stackoverflow.q68321439.annotation.MavenModuleInfo;
package de.scrum_master.stackoverflow.q68321439.application;
import de.scrum_master.stackoverflow.q68321439.annotation.MavenModuleInfo;
import de.scrum_master.stackoverflow.q68321439.library.LibraryClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class ModuleInfoTest {
@Test
public void test() {
String groupId = "de.scrum-master.stackoverflow.q68321439";
MavenModuleInfo libMavenInfo = logAndGetMavenModuleInfo("Library Maven info", LibraryClass.class.getPackage());
assertEquals(groupId, libMavenInfo.groupId());
assertEquals("library", libMavenInfo.artifactId());
MavenModuleInfo appMavenInfo = logAndGetMavenModuleInfo("Application Maven info", Application.class.getPackage());
assertEquals(groupId, appMavenInfo.groupId());
assertEquals("application", appMavenInfo.artifactId());
}
private MavenModuleInfo logAndGetMavenModuleInfo(String message, Package aPackage) {
MavenModuleInfo moduleInfo = aPackage.getAnnotation(MavenModuleInfo.class);
System.out.println(message);
System.out.println(" " + moduleInfo.groupId());
System.out.println(" " + moduleInfo.artifactId());
System.out.println(" " + moduleInfo.version());
return moduleInfo;
}
}
Run Maven build
Now run the Maven build via mvn clean test
:
(...)
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ application ---
[INFO] Surefire report directory: C:\Users\alexa\Documents\java-src\SO_Maven_ArtifactInfoRuntime_68321439\application\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running de.scrum_master.stackoverflow.q68321439.application.ModuleInfoTest
Library Maven info
de.scrum-master.stackoverflow.q68321439
library
1.0-SNAPSHOT
Application Maven info
de.scrum-master.stackoverflow.q68321439
application
1.0-SNAPSHOT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec
(...)
Identifying the caller
Assuming that all calling modules implement the same scheme with package info + special annotation, you can print the caller info like this:
package de.scrum_master.stackoverflow.q68321439.library;
import de.scrum_master.stackoverflow.q68321439.annotation.MavenModuleInfo;
public class LibraryClass {
public void doSomething() {
StackTraceElement callerStackTraceElement = new Exception().getStackTrace()[1];
try {
Class<?> callerClass = Class.forName(callerStackTraceElement.getClassName());
MavenModuleInfo mavenModuleInfo = callerClass.getPackage().getAnnotation(MavenModuleInfo.class);
System.out.println(mavenModuleInfo.groupId());
System.out.println(mavenModuleInfo.artifactId());
System.out.println(mavenModuleInfo.version());
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void doSomethingJava9() {
Class<?> callerClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass();
MavenModuleInfo mavenModuleInfo = callerClass.getPackage().getAnnotation(MavenModuleInfo.class);
System.out.println(mavenModuleInfo.groupId());
System.out.println(mavenModuleInfo.artifactId());
System.out.println(mavenModuleInfo.version());
}
}
While doSomething()
also works in old Java versions (tested on Java 8), on Java 9+ you can use the JEP 259 Stack-Walking API as shown in doSomethingJava9()
. In that case, you do not need to manually parse an exception stack trace and handle exceptions.
Solution C: Identifying the calling JAR via URL classloader
Assuming that you use my sample project and call the library from the application module (like in the previous section), a quick & dirty way to print JAR information would be this:
Add this method to LibraryClass
:
public void doSomethingClassLoader() {
StackTraceElement callerStackTraceElement = new Exception().getStackTrace()[1];
try {
Class<?> callerClass = Class.forName(callerStackTraceElement.getClassName());
// Cheap way of getting Maven artifact name - TODO: parse
System.out.println(
callerClass
.getClassLoader()
.getResource(callerStackTraceElement.getClassName().replaceAll("[.]", "/") + ".class")
);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Again, on Java 9+ you could make the code nicer by using the Stack-Walking API, see above.
Call the method from Application
:
public class Application {
public static void main(String[] args) {
// new LibraryClass().doSomething();
// new LibraryClass().doSomethingJava9();
new LibraryClass().doSomethingClassLoader();
}
}
Now build the Maven application from the command line and run with 3 different classpaths, pointing to
- the
target/classes
directory - the JAR in the
target
directory - the JAR in the local Maven repository
in order to see what kind of information gets printed to the console:
$ mvn install
(...)
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;application\target\classes" de.scrum_master.stackoverflow.q68321439.application.Application
file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/classes/de/scrum_master/stackoverflow/q68321439/application/Application.class
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;application\target\application-1.0-SNAPSHOT.jar" de.scrum_master.stackoverflow.q68321439.application.Application
jar:file:/C:/Users/alexa/Documents/java-src/SO_Maven_ArtifactInfoRuntime_68321439/application/target/application-1.0-SNAPSHOT.jar!/de/scrum_master/stackoverflow/q68321439/application/Application.class
$ java -cp "annotation\target\annotation-1.0-SNAPSHOT.jar;library\target\library-1.0-SNAPSHOT.jar;c:\Users\Alexa\.m2\repository\de\scrum-master\stackoverflow\q68321439\application\1.0-SNAPSHOT\application-1.0-SNAPSHOT.jar" de.scrum_master.stackoverflow.q68321439.application.Application
jar:file:/C:/Users/alexa/.m2/repository/de/scrum-master/stackoverflow/q68321439/application/1.0-SNAPSHOT/application-1.0-SNAPSHOT.jar!/de/scrum_master/stackoverflow/q68321439/application/Application.class
As you can see
- in case 1, you can indirectly infer the Maven artifact from the project path,
- in case 2, you see artifact ID and version in the JAR name and the group ID indirectly in the project path,
- in case 3, you see artifact ID and version in the JAR name and the group ID directly in the Maven repository path.
Of course, you could parse that information and print it in a more structured way, but I suggest to simply print it like this and let the human brain reading the log do the parsing.
Like I said in a comment before, this works nicely in the case I showed you, also with different projects, not just in a single multi-module project. What kinds of information you would see in case of an application server deployment or uber JAR situation, strongly depends on the exact situation. There is no single, generic answer, and I cannot do your whole job for you. I showed you several options, now you can select one.
How to get Maven project version to the bash command line
The Maven Help Plugin is somehow already proposing something for this:
help:evaluate
evaluates Maven expressions given by the user in an interactive mode.
Here is how you would invoke it on the command line to get the ${project.version}
:
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
-Dexpression=project.version
As noted in the comments by Seb T, to only print the version without the maven INFO logs, additionally use -q -DforceStdout
:
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
Get Maven artifact version at runtime
You should not need to access Maven-specific files to get the version information of any given library/class.
You can simply use getClass().getPackage().getImplementationVersion()
to get the version information that is stored in a .jar-files MANIFEST.MF
. Luckily Maven is smart enough Unfortunately Maven does not write the correct information to the manifest as well by default!
Instead one has to modify the <archive>
configuration element of the maven-jar-plugin
to set addDefaultImplementationEntries
and addDefaultSpecificationEntries
to true
, like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
</archive>
</configuration>
</plugin>
Related Topics
How to Create 2 Separate Log Files with One Log4J Config File
How to Schedule a Task to Run at Periodic Intervals
Missing Artifact Com.Sun:Tools:Jar
How to Avoid Constructor Code Redundancy in Java
How Is Countdownlatch Used in Java Multithreading
How to Extract Cn from X509Certificate in Java
Spark Read File from S3 Using Sc.Textfile ("S3N://...)
Why Is My Uri Not Hierarchical
Difference Between List, List<>, List<T>, List<E>, and List<Object>
Why Do We Need Immutable Class
How Can a Class Have a Member of Its Own Type, Isn't This Infinite Recursion
Instanceof - Incompatible Conditional Operand Types
Is String Literal Pool a Collection of References to the String Object, or a Collection of Objects
Debug a Java Application Without Starting the Jvm with Debug Arguments