I am currently developing a Spring Boot Starter which will host a Restful web service with some meta-data about the running application.
I am having difficulties extracting my artifactId and versionId from my mainfest file. I believe my issue is that the autoconfiguration classes are being loaded before the main Test application so the manifest is not yet available to be discovered. I am not sure if my logic here is correct of if I am approaching the problem from the wrong angle.
I originally followed the following tutorial for setup.
This gave me 3 separate projects
Generic Spring Services with no context
AutoConfiguration project for these services
Spring Boot starter
I paired the starter with a test project as an end result.
Currently maven is being used with Spring Boot to generate a manifest file.
Implementation-Title: MyExampleProjectWithCustomStarter
Implementation-Version: 0.0.1-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: mcf
Implementation-Vendor-Id: com.coolCompany
Spring-Boot-Version: 1.5.4.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.coolcompany.SpringBootExampleApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_131
Implementation-URL: http://someurl
However, when I attempt to locate the manifest file for the Example project from my generic service package I cannot find the file.
private String getApplicationVersion(String applicationName, List<Attributes> manifests) {
String unknownVersion = "0.0.0-UNKNOWN";
for (Attributes attr : manifests) {
String title = attr.getValue(IMPL_TITLE);
String version = attr.getValue(IMPL_VERSION);
if (version != null) {
if (applicationName.equalsIgnoreCase(title)) {
return title + ' ' + version;
}
}
}
log.warn(
"Could not find MANIFEST file with '" + applicationName + "' as Implementation-Title."
+ " Meta-API will return buildVersion '" + unknownVersion + "'.");
return applicationName + ' ' + unknownVersion;
}
private List<Attributes> loadManifestFiles() {
List<Attributes> manifests = new ArrayList<>();
try {
Enumeration<URL> resources =
Thread.currentThread().getContextClassLoader().getResources("/META-INF/MANIFEST.MF");
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
try (InputStream is = url.openStream()) {
manifests.add(new Manifest(is).getMainAttributes());
System.out.println("Manifest size:" + manifests.size());
} catch (IOException e) {
log.error("Failed to read manifest from " + url, e);
}
}
} catch (IOException e) {
log.error("Failed to get manifest resources", e);
}
return manifests;
}
Current manifest Implementation-Titles:
Spring Boot Web Starter
Spring Boot Starter
Spring Boot
Spring Boot AutoConfigure
Spring Boot Logging Starter
null
null
jcl-over-slf4j
null
log4j-over-slf4j
null
Spring Boot Tomcat Starter
Apache Tomcat
Apache Tomcat
Apache Tomcat
hibernate-validator
null
JBoss Logging 3
ClassMate
jackson-databind
Jackson-annotations
Jackson-core
spring-web
spring-aop
spring-beans
spring-context
spring-webmvc
spring-expression
Spring Boot Actuator Starter
Spring Boot Actuator
null
** MyCustom-spring-boot-starter
** MyGenericSpringService
null
null
null
Metrics Core
JVM Integration for Metrics
null
null
Jackson datatype: JSR310
** MyService-spring-boot-autoconfigure
slf4j-api
spring-core
** Missing MyExampleProjectWithCustomStarter
count of manifest records: 44
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.
You can find the Spring Framwork version of spring-boot-starter-X by checking its pom. xml . Navigate to its parent pom until you get to spring-boot-dependencies-VERSION. pom .
Artifact: project coordinates (id of the artifact, as referred by the artifactId attribute in Apache Maven). Also infers the name of the project. Name: display name of the project that also determines the name of your Spring Boot application.
After a lot of effort, I found a surprisingly simple answer. This is how spring-boot-actuator gets the information.
The Spring Boot Maven plugin comes equipped with a build-info goal. As long as this goal is triggered in the main project Spring has a BuildProperties class you can wire in for the information.
<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>
You can access the properties in your starter like:
@Autowired
BuildProperties buildProperties;
...
buildProperties.getArtifact();
buildProperties.getVersion();
You can even specify additional properties from the plugin. See the plugin documentation for more details: https://docs.spring.io/spring-boot/docs/current/maven-plugin/build-info-mojo.html
Unfortunately I never quite got to fully understand why I could not access the correct manifest, but this should help anyone else trying to solve this problem.
The other answer is completely correct. Just for others finding this question in case you are using Gradle instead of Maven:
Generating build info is as simple as adding this to your build.gradle
file:
plugins {
id 'org.springframework.boot' version '<your-boot-version>.RELEASE'
}
// ...
springBoot {
buildInfo()
}
And if you want to pass custom properties:
springBoot {
buildInfo {
properties {
additional = [
'property.name': 'property value',
'other.property': 'different.value'
]
}
}
}
Then the usage in Java code is the same using BuildProperties
. You can find more info about the plugin in this guide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With