Detecting build version and time at runtime in Spring Boot

Jun 25, 2018
Detecting build version and time at runtime in Spring Boot
How to obtain artifact version, build time and other build information in a Spring Boot app at runtime?

This post covers obtaining simple build-related information without adding any additional dependencies. For much more detailed info, various metrics and health monitoring you should consider using Spring Boot Actuator:

Actuator: Spring Boot Production Monitoring and Management

Obtaining build information

It can often be useful to obtain information about artifact, version, build time and other at runtime. Sure, most of this information is already in your pom.xml file, but it can be tricky to retrieve these when the application is running.

Having such information at runtime can be useful. For example, imagine a scenario, where you expose a REST endpoint, which can tell the client what your current version of the application is, when was it built and so on. It can be useful because you can quickly determine what version of the app is currently deployed. This can be especially important in non-production environments, where the app is frequently deployed or even with continuous deployment in production. In such cases, it is vital to know what build exactly is currently running when testing and submitting bug reports. Maybe the issue reported is already fixed in a newer version or maybe the bug still occurs because the new version is implemented, but not deployed yet.

In any case, having build information can be handy and it is useful to know how to obtain it at runtime. In Spring Boot, it is fortunately quite easy.

Build plugin configuration

If you are using Spring Boot, your pom.xml should already contain spring-boot-maven-plugin. You just need to add the following configuration.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>        <execution>            <id>build-info</id>            <goals>                <goal>build-info</goal>            </goals>        </execution>    </executions></plugin>

It instructs the plugin to execute also build-info goal, which is not run by default. This generates build meta-data about your application, which includes artifact version, build time and more.

If you are using Gradle, just add the following to your build.gradle file:

springBoot {
    buildInfo()
}

Accessing Build Properties

After configuring your spring-boot-maven-plugin and building your application, you can access information about your application's build through BuildProperties object. Let the Spring inject it for you:

@Autowired
BuildProperties buildProperties;

Now you can access various information from this object.

// Artifact's name from the pom.xml file
buildProperties.getName();
// Artifact version
buildProperties.getVersion();
// Date and Time of the build
buildProperties.getTime();
// Artifact ID from the pom file
buildProperties.getArtifact();
// Group ID from the pom file
buildProperties.getGroup();

Adding custom properties

If predefined properties are not enough, you can pass your own properties from pom.xml file to BuildProperties.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>build-info</goal>
            </goals>
            <configuration>                <additionalProperties>                    <java.version>${java.version}</java.version>                    <some.custom.property>some value</some.custom.property>                </additionalProperties>            </configuration>        </execution>
    </executions>
</plugin>

You can pass a value directly or use your custom properties defined in the <properties> section of your pom.xml and then referenced using ${property.name} placeholder.

You can access custom properties defined this way by calling buildProperties.get("property.name").

For Gradle projects, custom properties can be defined this way:

springBoot {
    buildInfo {
        properties {
            additional = [
                'property.name': 'property value',
                'other.property': 'different.value'
            ]
        }
    }
}

How it works under the hood

When build-info of spring-boot-maven-plugin is run, it generates a property file containing all the build information. By default, it is located at ${project.build.outputDirectory}/META-INF/build-info.properties, but you can customize it by providing outputFile parameter. The file looks something like this:

#Properties
#Sat Jun 23 15:58:56 CEST 2018
build.version=0.0.1-SNAPSHOT
build.group=com.vojtechruzicka
build.name=spring-rest-docs-example
build.artifact=spring-rest-docs-example
build.time=2018-06-23T13\:58\:56.742472800Z

When Spring detects there is this file on the classpath, it creates BuildProperties bean unless it is explicitly declared. This is configured in org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration. This is a nice example of Spring Boot Auto-Configuration, where certain beans can be created just by having specific files on the classpath.

@ConditionalOnResource(resources = 
"${spring.info.build.location:classpath:META-INF/build-info.properties}")
@ConditionalOnMissingBean
@Bean
public BuildProperties buildProperties() throws Exception {
    return new BuildProperties(
            loadFrom(this.properties.getBuild().getLocation(), 
            "build"));
}

Detecting Spring profiles

It is no doubt useful to know which version of your artifact is deployed and when it was built. However, it is usually not enough. Often Spring applications use various profiles, which can significantly change the behavior. Typical usage is, for example, having a separate profile for each environment (DEV, UAT, PROD, ...). Depending on the profile, the correct environmental configuration can be loaded such as DB connection and more.

It is useful to be able to determine current profiles as sometimes the app can be run with different profiles than expected. To detect the current profiles, you need just to inject Environment object and then you can simply obtain them by calling getActiveProfiles().

@Autowired
private Environment environment;

environment.getActiveProfiles();

What's more, since you already have environment object, you can obtain any environmental properties by calling environment.getProperty("property.name").

Spring Actuator & Admin

While this approach gives you basic build and version info, sometimes you may need a more powerful tool. Spring Boot Actuator is a sub-project of Spring Boot, which adds some production-grade monitoring and management tools exposed as REST and JMX endpoints. In fact, it can easily be configured to use build information provided by BuildProperties and provide them through one of its endpoints.

Spring Boot Admin is a community project, which provides a nice user interface on top of Spring Actuator endpoints, so the app is more comfortable to manage through a nice admin GUI.

Conclusion

Having access to version and build information at runtime can be quite useful. In Spring boot application, you can easily obtain the info by altering the Spring Boot Maven/Gradle plugin configuration to generate the build.properties file and then accessing it through BuildProperties object.

For simple scenarios, this is an easy and quick solution and should work for you. If you need something more powerful, look at Spring Actuator or Spring Admin, which can provide the build metadata functionality plus a lot more.




Let's connect