1

I'm working on a Spring Boot MVC application. The requirement is to read the MANIFEST.MF file to get build number and application version number etc of this spring boot app.

For this, I have written the following bean definition for Manifest Class so that I can autowire it in the Controller class.

@Configuration
public class AppConfig{
    
    @Bean("manifest")
    public java.util.jar.Manifest getManifest()
    {
        // get the full name of the application manifest file
        String appManifestFileName = this.getClass().getProtectionDomain().getCodeSource().getLocation().toString() + JarFile.MANIFEST_NAME;
        Enumeration resEnum;
        try
        {
            // get a list of all manifest files found in the jars loaded by the app
            resEnum = Thread.currentThread().getContextClassLoader().getResources(JarFile.MANIFEST_NAME);
            while (resEnum.hasMoreElements())
            {
                try
                {
                    URL url = (URL) resEnum.nextElement();
                    System.out.println("Resource url=" + url.toString());
                    // is the app manifest file?
                    if (url.toString().equals(appManifestFileName))
                    {
                        // open the manifest
                        InputStream is = url.openStream();
                        if (is != null)
                        {
                            // read the manifest and return it to the application
                            Manifest manifest = new Manifest(is);
                            return manifest;
                        }
                    }
                }
                catch (Exception e)
                {
                    // Silently ignore wrong manifests on classpath?
                }
            }
        }
        catch (IOException e1)
        {
            // Silently ignore wrong manifests on classpath?
        }
        return null;
    }
}

The above code is taken from here. But it didn't help. It is always giving me null object.

AppController.java

@RestController
public class AppController
{    
    @Autowired
    private Environment env;
    
    @Autowired
    @Qualifier("manifest")
    private Manifest manifest;
    
    @GetMapping("/get-app-details")
    public String getAppDetails()
    {
        Attributes mainAttributes = manifest.getMainAttributes();
        String buildNum = mainAttributes.getValue("Build-Number");
        buildNum = buildNum.substring(buildNum.lastIndexOf('_') + 1);
        String AppVersion = env.getProperty("App.Version") + "." + buildNum;
        return "Build Number - " + buildNum + ", AppVersion - " + AppVersion;
    }
}

Additional info - I'm using gradle to build this application as a war file and deploying it into external tomcat version 9.

E_net4
  • 27,810
  • 13
  • 101
  • 139
Ravi
  • 1,614
  • 4
  • 14
  • 27
  • Have you looked at the [javadoc of the `Manifest` class](https://docs.oracle.com/javase/8/docs/api/java/util/jar/Manifest.html)? That will show you what constructors are available. However I would suggest to ditch this approach and use the Spring Boot actuator with the info endpoint to expose this information. See https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.info – M. Deinum Jun 16 '22 at 05:27
  • @M.Deinum Thanks. But I have tried `InputStream inputFile = new FileInputStream("/META-INF/MANIFEST.MF"); Manifest manifestObj = new Manifest(inputFile);` but I'm getting `java.io.FileNotFoundException: \META-INF\MANIFEST.MF (The system cannot find the path specified)` – Ravi Jun 16 '22 at 06:07
  • It isn't a file it is a resource in the classpath. – M. Deinum Jun 16 '22 at 06:10
  • File Location is : `src/main/webapp/META-INF/MANIFEST.MF`. I even tried with `FileInputStream("/webapp/META-INF/MANIFEST.MF")`. But no luck – Ravi Jun 16 '22 at 06:14
  • Again it isn't a file on the resource. It is read from within your jar/war file. It doesn't read it while building, it reads it while running. So it isn't a physical file on the file system at that point but a resource inside an archive. – M. Deinum Jun 16 '22 at 06:14
  • Sorry, I didn't get you. Can you please elaborate more – Ravi Jun 16 '22 at 06:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245651/discussion-between-ravi-kumar-b-and-m-deinum). – Ravi Jun 16 '22 at 06:19
  • 2
    Not really a `java.io.File` (which is what is used underneath) needs to be a physical file somewhere on the file system. If you package a file inside a jar/war/ear/zip it isn't a physical file anymore and as such cannot be loaded with `java.io.File` anymore. You need to load it as a stream/resource. Something like `getClassLoader().getResourceAsStream(/META-INF/MANIFEST.MF)`. Or just use the `ResourceLoader` abstraction from Spring `new ClassPathResource("/META-INF/MANIFEST.MF).getInputStream()). – M. Deinum Jun 16 '22 at 06:21
  • @M.Deinum, Thanks a lot. I'm able to achieve what I wanted. – Ravi Jun 16 '22 at 06:41
  • @M.Deinum, I have updated the question as this is what I wanted to achieve finally. Could you please have a look and let me know if you can help me.? – Ravi Sep 14 '22 at 13:20
  • Please don't change your question after a solution that worked. I also suggested to use `new ClassPathResouce` instead of you loading it yourself. However I probably would say you should stop this and use the info endpoint solution available in Spring Boot, which can append/read the values from a file. – M. Deinum Sep 14 '22 at 13:24
  • @M.Deinum, what do you mean by endpoint solution?. Could you please write an answer to this question instead of comment as I'm having difficulty in understanding what you are saying. – Ravi Sep 15 '22 at 03:57
  • Spring Boot (or actually the actuator) has an info endpoint, which at build time can add the version information to that endpoint. See https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.endpoints.info on how to use that. – M. Deinum Sep 15 '22 at 05:17
  • You cannot write to a `Manifest` as it is part of the classpath. I would strongly suggest to ask a new question and describe **what** you want and then **how you tried** to do it and why it doesn't work. YOu have not described a **how** and not a **what** with unclear comments referring to your original question. – M. Deinum Sep 15 '22 at 08:05
  • @M.Deinum, sorry my bad. I don't to want to write anything into Manifest but want to read data from META/MANIFEST.MF that we add while building the war file using gradle. Sure. I'll create a new question with more details on what I want and how I'm trying etc. – Ravi Sep 15 '22 at 08:16
  • Which you can do with the info endpoint and write it to another file. That way SPring Boot Actuator will handle all that for you. – M. Deinum Sep 15 '22 at 08:17
  • @M.Deinum, I have created separate [question here](https://stackoverflow.com/q/73740077/9145082). Can you please have a look? – Ravi Sep 16 '22 at 05:08

0 Answers0