Why would ClassLoader.getResourceAsStream() return null?

15,045

Solution 1

I solved the mystery.

The key to solving was embedding some diagnostic logging when propertiesStream is null:

String classpath = System.getProperty("java.class.path");
LOG.info("CLASSPATH: " + classpath);
ClassLoader loader = MyClientMain.class.getClassLoader();
System.out.println("ClassLoader resource path: " + loader.getResource("resource.properties"));                    

So when I run with the original

contextClassLoader.getResourceAsStream("resource.properties")

I receive the null pointer condition, printing:

  INFO: CLASSPATH: myproj.one-jar.jar
  ClassLoader resource path: null

.

I then started suspecting something related to the "jar within a jar" as this is what the com.simontuffs.onejar essentially does (i.e. wrapping my project's jar inside a jar that contains all other library jars), so I opened myproj.one-jar.jar with 7-Zip and noted the full (absolute) path of "resource.properties":

myproj.one-jar.jar\main\myproj.jar\webapp\WEB-INF\classes\resource.properties

.

So I modified getResource("resource.properties") to:

 getResource("/main/myproj.jar/webapp/WEB-INF/classes/resource.properties")

which didn't fix the problem but printed the following upon the null pointer condition:

INFO: CLASSPATH: myproj.one-jar.jar
ClassLoader resource path: jar:file:/myproj.one-jar.jar!/main/myproj.jar!//main/myproj.jar/webapp/WEB-INF/classes/resource.properties

.

Then... divine intervention fell upon me and I had the insight (not reading any documentation that could even hint this, I swear!) that I should be using this path instead:

 getResource("/webapp/WEB-INF/classes/resource.properties")

And Voila! It works.

Whew.

Solution 2

As EJP pointed out, it means that the resource isn't available via the classpath for this particular classloader (different classloaders can have different classpaths).

Since the classloader is a JarClassLoader, it will only be able to load resources that are included inside the jar file. It won't see files that are in the same directory as the jar file.

Share:
15,045

Related videos on Youtube

Withheld
Author by

Withheld

Updated on June 17, 2022

Comments

  • Withheld
    Withheld almost 2 years

    Having the following code broken deliberately to identify the source of a NullPointerException in something that should have been very simple but turns out to drive me nuts:

    Properties properties = new Properties();
    Thread currentThread = Thread.currentThread();
    ClassLoader contextClassLoader = currentThread.getContextClassLoader();
    InputStream propertiesStream = contextClassLoader.getResourceAsStream("resource.properties");
    if (propertiesStream != null) {
      properties.load(propertiesStream);
      // TODO close the stream
    } else {
      // Properties file not found!
    }
    

    I get the "Properties file not found!" error, i.e. contextClassLoader.getResourceAsStream("resource.properties"); returns null.

    This is a CXF-based client and I verified that the "resource.properties" file is in the current directory in which the client's jar resides (and runs).

    I also verified the absolute path by including the following diagnostic code:

                File file = new File("resource.properties");
                System.out.println(file.getAbsolutePath());
    

    The absolute path points to where the client's jar is.

    I also tried finding out the context of the class loader, using:

      System.out.println(Thread.currentThread().getContextClassLoader());
    

    but instead some directory structure as demonstrated here, all I get is:

    com.simontuffs.onejar.JarClassLoader@1decdec

    Why would ClassLoader.getResourceAsStream() return null?

    What am I missing?

    • user207421
      user207421 about 10 years
      Because the named resource isn't available via the CLASSPATH.
  • Withheld
    Withheld about 10 years
    Thank you and that's exactly what I originally did. I put the file in both the src/main/resources and the webapp/WEB-INF/classes directories of the project, properly included in the JAR file. To no avail. What am I missing? Is it possible that the com.simontuffs.onejar.JarClassLoader modifies the classpath in some way? If so, how do I find the de-facto classpath?
  • Kayaman
    Kayaman about 10 years
    Check that the resource.properties file is included inside the jar file (if you wish to use that classloader to load it). Also try to load it as /resource.properties (if it's in the root of the jar).
  • Withheld
    Withheld about 10 years
    Yes, it is included (in both places, at least for the debug phase) and I tried both resource.properties and /resource.properties. To no avail. Something wicked is going on. I am suspecting something very specific to that "simontuffs"... How can I print the current classpath as the client application sees it at runtime?
  • Kayaman
    Kayaman about 10 years
    There is no single classpath. It all depends on the ClassLoader. It can delegate to its parent ClassLoader if it can't find it by itself, but I suspect that JarClassLoader (like URLClassLoader) doesn't delegate to its parent. You could use the getPackages() method to see which packages the classloader knows, and then make sure that the resource is in the jar at the proper sub directory (i.e. if the classloader knows a package com.foo.bar, the file should be inside the jar in com/foo/bar/resource.properties).
  • Withheld
    Withheld about 10 years
    I just used Package.getPackages() and it prints the world... i.e. hundreds of packages, but no path or a hint to a path. Still trying to figure out the proper sub directory. Additional suggestions on how to figure this out are much appreciated.
  • Kayaman
    Kayaman about 10 years
    Well, packages are also paths. Classes in package com.foo.bar will be in com/foo/bar/.