How to read data from META/MANIFEST.MF in Spring Boot MVC Web Application?
Asked Answered
A

1

2

I have a requirement to read information that is available in the META/MANIFEST.MF file of Spring Boot MVC web application and use this info to perform some business logic. I'm using gradle to build the application as war file and deploying it into the external tomcat.

I have tried the following:

@Configuration
public class AppConfig
{
    @Bean("manifest")
    public java.util.jar.Manifest getManifest() throws IOException
    {
        InputStream inputFile = this.getClass().getClassLoader().getResourceAsStream("META-INF/MANIFEST.MF");
        Manifest manifestObj = new Manifest(inputFile);
        return manifestObj;
    }
}

AppService.java

@Service
public class AppService
{        
    @Autowired
    @Qualifier("manifest")
    private Manifest manifest;
    
    @PostConstruct
    public String init()
    {
        Attributes mainAttributes = manifest.getMainAttributes();
        String buildNum = mainAttributes.getValue("Build-Number");
        String customPropInfo= mainAttributes.getValue("customPropInfo");
        String systemPrp1= buildNum + "_" + "SomeBusinessInfoLogic1";
        String systemPrp2= customPropInfo+ "_" + "SomeBusinessInfoLogic2";
        //Some Business Logic with these attributes systemPrp, systemPrp2
        logger.info("System Props are updated");
    }
}

I'm getting null for both buildNum and customPropInfo.

Note: I have tried creating the Manifest bean something like this which was created by me. As per the @M.Deinum suggestion I'm creating this new question here. I also tried the solutions here which didn't work for me.

@M.Deinum suggested to make use of Spring Boot's Actuator Info endpoint. But this endpoint is useful when we want to access the info outside of the application but my requirement is different as I need the data that is available in MANIFEST.MF file to perform some business operations within the application.

I get the following error when I tried this solution "/META-INF/MANIFEST.MF".

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'manifest' defined in class path resource [com/abc/AppConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.util.jar.Manifest]: Factory method 'getManifest' threw exception; nested exception is java.lang.NullPointerException: Cannot invoke "java.io.InputStream.read(byte[], int, int)" because "this.in" is null

Can someone please help me to read information from META/MANIFEST.MF of the Spring Boot MVC Web Application?.

UPDATE1: I get the following MainAttributes when I try to print MainAttributes. But the problem is when I try to deploy the war into external tomcat.

System.out.println("Manifest MainAttributes = " +manifestObj.getMainAttributes().keySet());

Output:

Manifest MainAttributes = [Manifest-Version, Implementation-Title, Automatic-Module-Name, Implementation-Version, Built-By, Spring-Boot-Jar-Type, Build-Jdk-Spec]

UPDATE2: I have updated to AppService.java to print the info available in autowired Manifest object. Something like below:

@Configuration
public class AppConfig
{
    @Bean("manifest")
    public java.util.jar.Manifest getManifest() throws IOException
    {
        InputStream inputFile = new ClassPathResource("/META-INF/MANIFEST.MF").getInputStream();
        Manifest manifestObj = new Manifest(inputFile);
        System.out.println("Manifest Manifest-Version = " +manifestObj.getMainAttributes().getValue("Manifest-Version"));
        System.out.println("Manifest KeySet = " +manifestObj.getMainAttributes().keySet());
        return manifestObj;
    }
}
@Service
public class AppService
{        
    @Autowired
    @Qualifier("manifest")
    private Manifest manifest;
    
    @PostConstruct
    public String init()
    {
        Attributes mainAttributes = manifest.getMainAttributes();
        mainAttributes.forEach((k,v) -> {
            System.out.println("AppService.init(): Key = "+k+", Value = "+v);
        });
        String buildNum = mainAttributes.getValue("Build-Number");
        String customPropInfo= mainAttributes.getValue("customPropInfo");
        String systemPrp1= buildNum + "_" + "SomeBusinessInfoLogic1";
        String systemPrp2= customPropInfo+ "_" + "SomeBusinessInfoLogic2";
        //Some Business Logic with these attributes systemPrp, systemPrp2
        logger.info("System Props are updated");
    }
}

I see the following output on the console:

AppService.init(): Key = Implementation-Title, Value = Apache Tomcat Bootstrap
AppService.init(): Key = Implementation-Version, Value = 9.0.12
AppService.init(): Key = Specification-Vendor, Value = Apache Software Foundation
AppService.init(): Key = Specification-Title, Value = Apache Tomcat Bootstrap
AppService.init(): Key = Class-Path, Value = commons-daemon.jar
AppService.init(): Key = Manifest-Version, Value = 1.0
AppService.init(): Key = Main-Class, Value = org.apache.catalina.startup.Bootstrap
AppService.init(): Key = Implementation-Vendor, Value = Apache Software Foundation
AppService.init(): Key = Ant-Version, Value = Apache Ant 1.9.9
AppService.init(): Key = X-Compile-Target-JDK, Value = 1.8
AppService.init(): Key = X-Compile-Source-JDK, Value = 1.8
AppService.init(): Key = Created-By, Value = some xyz
AppService.init(): Key = Specification-Version, Value = 9.0

So just by the above output, I think MANIFEST.MF is not application specific but is from commons-daemon.jar.

Attired answered 16/9, 2022 at 5:6 Comment(7)
Q: Does your Manifest constructor actually READ from the Input Stream (for example, read the contents of manifest.mf into a HashMap)? It looks like you do - and that's where the exception occurs. Q: Do you ever CLOSE the stream? Q: Have you STEPPED THROUGH the relevant parts of your code in a debugger yet to confirm which parts "work", and which don't? Most important: Please show us the relevant parts of the code: presumably the constructor, and perhaps also getMainAttributes().Cornemuse
For Q1, yes, I verified it by putting some content into it manually and ran using maven. But the problem is when I build the project with gradle to generate war and deployed into tomcat that's when it fails fails reading the MANIFEST.MF file. I mean with embedded tomcat (Jar) it works but not when created war and deployed into external tomcat. I have even verified that MANIFEST.MF is present in war and it contains the info that I wanted to read.Attired
You shoild be loading /META-INF/MANIFEST.MF (absolute) instead of "META-INF/MANIFEST.MF". Instead of doing what you are doing now use a ClassPathResource like new ClassPathResource("/"META-INF/MANIFEST.MF").getInputStream() instead what you have now. And make sure that casing of the name is correct so if it is manifest.mf it won't load as currently it finds MANIEFEST.MF.Bechtel
@M.Deinum, I have updated the question but trying your suggestion ie. after I changed the code to use InputStream inputFile = new ClassPathResource("/META-INF/MANIFEST.MF").getInputStream();. But issue is not resolved as it read from some random jar file.Attired
I doubt you can solve that as it will read the first MANIFEST it finds on the classpath (that is how reading from the classpath works). Do you really need to add it to the MANIFEST. Why not a dedicated properties file that you read (or enrich your application.properties instead of your MANIFEST. That way you don't have to do anything and Spring Boot will make those properties available.Bechtel
@M.Deinum, I think that's good idea to add whatever info that we are adding to MANIFEST.MF into application.properties so that we read them. I have searched for articles to read and update application.properties file in gradle script but could not find any. Can you please point me to any article that you to do this job?Attired
I'm not sure how to do that in Gradle, I know in Maven you can proces resources and replace properties at build time. I'm sure Gradle has something like that as well.Bechtel
C
2

OK - the problem isn't that you "can't read data from META/MANIFEST.MF in Spring Boot MVC Web Application". Rather, the problem is that your code happens to be reading the WRONG MANIFEST.MF from some other, random .jar in the classpath.

One solution might be to use JarClassLoader.

Another solution, as M. Deinum suggested, might be to store the properties you wish to retrieve in application.properties (or some other "global" properties file) instead of MANIFEST.MF.

ALSO: I assume you're probably using an IDE to develop your app (Eclipse, Netbeans, etc). If you haven't already, I would STRONGLY encourage you to familiarize yourself with your IDE's debugger: the ability to set breakpoints, display variables, single-step through method calls, etc.

Cornemuse answered 16/9, 2022 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.