How to read project specific META/MANIFEST.MF in Spring Boot Web Application?
Asked Answered
H

0

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.

Heterogenetic answered 16/6, 2022 at 5:4 Comment(17)
Have you looked at the javadoc of the Manifest class? 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 docs.spring.io/spring-boot/docs/current/reference/html/…Numismatology
@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)Heterogenetic
It isn't a file it is a resource in the classpath.Numismatology
File Location is : src/main/webapp/META-INF/MANIFEST.MF. I even tried with FileInputStream("/webapp/META-INF/MANIFEST.MF"). But no luckHeterogenetic
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.Numismatology
Sorry, I didn't get you. Can you please elaborate moreHeterogenetic
Let us continue this discussion in chat.Heterogenetic
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()).Numismatology
@M.Deinum, Thanks a lot. I'm able to achieve what I wanted.Heterogenetic
@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.?Heterogenetic
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.Numismatology
@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.Heterogenetic
Spring Boot (or actually the actuator) has an info endpoint, which at build time can add the version information to that endpoint. See docs.spring.io/spring-boot/docs/current/reference/htmlsingle/… on how to use that.Numismatology
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.Numismatology
@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.Heterogenetic
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.Numismatology
@M.Deinum, I have created separate question here. Can you please have a look?Heterogenetic

© 2022 - 2024 — McMap. All rights reserved.