Search code examples
javaspringmavenspring-bootmanifest

Getting ArtifactId and Version in Spring Boot Starter


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


Solution

  • 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.