1

I'm facing one odd issue with loading a properties file from a static class/method which is inside a jar file. This jar file is part of a spring boot application. So basically following is the structure

API-springboot.jar
|
|---- BOOT-INF/lib/my-other.jar
      |
      |----- app.properties
      |----- com/foo/bar/blah/Utils.class --> Static class loading app.properties
  • Here Utils.class is a collection of various static utility methods.
  • Util.class has a static method which loads the app.properties. Below is the code of Utils
package com.foo.bar.blah;

public final class Utils {
    private static final String PROPS_FILE = "app.properties";
    private static final Properties props = loadProperties();

    public static String doSomething(String str) {
        // ... some logic by reading props
        return "blah";
    }

    private static Properties loadProperties() {
        LOG.debug("Loading properties {}", PROPS_FILE);
        InputStream resourceAsStream = Thread.currentThread()
                .getContextClassLoader()
                .getResourceAsStream(PROPS_FILE);

        if (resourceAsStream == null) {
            throw new RuntimeException("Can't load the properties file " + PROPS_FILE);
        }

        Properties properties = new Properties();
        try {
            properties.load(new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8));
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }

        return properties;
    }
}
  • Now when the Utils.doSomething is invoked for the first time, loadProperties will be invoked and load app.properties (at the root of the classpath)

  • This works fine locally (macOS & OpenJDK 11.0.8) and also on my test env (RHEL 7 and OpenJDK 11.0.9)

  • When deployed on UAT env, it cannot find app.properties and throws an exception as defined in the method

    if (resourceAsStream == null) {
        throw new RuntimeException("Can't load the properties file " + PROPS_FILE);
    }
  • Spring boot version is v2.3.5.RELEASE

  • Below is what I've checked till now

    • On UAT, I have the same JRE as on test
    • UAT and test has the same version of the application deployed via CI/CD pipeline (Jenkins/Artifactory etc)
    • On UAT, I extracted my application's spring-boot jar file and can see the my-other.jar under BOOT-INF/lib
    • I verified that my-other.jar has the app.properties in the root and also the Utils.class is present
    • I created a sample test class on one of the UAT servers which simply calls Utils.doSomething("blah") with correct classpath set and it can load the app.properties from the jar and shows expected result. But the issue comes when it's invoked from the spring-boot application.
    • JRE version is the same on UAT and test and spring-boot application is using the right JRE.
    openjdk version "11.0.9" 2020-10-20 LTS
    OpenJDK Runtime Environment 18.9 (build 11.0.9+11-LTS)
    OpenJDK 64-Bit Server VM 18.9 (build 11.0.9+11-LTS, mixed mode, sharing)

Question: What is causing this behaviour? Am I missing anything? Spent 3 hours with no success.

Jay
  • 1,539
  • 1
  • 16
  • 27
  • No am not stuck with the jar file as it's my own. But am more curious about what's causing it to work locally and test env and not on UAT? Test and UAT has exact same release, same java version, same spring-boot version, then why it doesn't;'t work. It makes me sleepless :-( – Jay Nov 24 '20 at 16:29
  • 1
    see https://stackoverflow.com/q/1771679/217324 for ideas about why this might fail. maybe threadpooling is set up differently? – Nathan Hughes Nov 24 '20 at 18:44
  • Thanks for sharing the SO link, quite interesting. Regarding threadpooling, no, it's same. I think I had current class loader previously but if my memory goes well, I had some remark from sonar scan and it suggested to use thread's context class loader and it made sonar happy and was working fine, so I didn't bother. Will need some time before I deploy changes to higher env, but will get back to you – Jay Nov 24 '20 at 20:50
  • Yes, it was PMD rule - https://pmd.github.io/pmd-6.1.0/pmd_rules_java_errorprone.html#useproperclassloader. – Jay Nov 24 '20 at 21:01
  • That does not look like the best advice to me. Context classloaders address a very specific issue, and unless there's something else beside reading a property file involved, i don't think you're having that issue. Try calling getResourceAsStream on the class, and don't use the classloader, and maybe PMD won't freak out. – Nathan Hughes Nov 24 '20 at 21:28
  • Indeed it's not good advice from PMD and that too critical. Maybe I need to talk to the team in my company who sets this rule and ask to take it off or lower the severity. Anyway, mine is not a J2EE app. Regarding not using classloader, didn't help, it's not able to load the props file (mostly because am using it in static context) – Jay Nov 24 '20 at 21:35
  • "static context" doesn't seem relevant. is this class loaded by the application or is it in a shared classloader at a higher level or something? – Nathan Hughes Nov 24 '20 at 21:48
  • I meant from a static method. Regarding classloader, am not able to find. Don't have JDK on UAT env, only JRE. Any hints on how can I find it? – Jay Nov 25 '20 at 13:03
  • Reverted back to `Utils.class.getClassLoader()` helped. – Jay Nov 25 '20 at 20:04

0 Answers0