Why would ClassLoader.getResourceAsStream() return null?
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.
Related videos on Youtube
Withheld
Updated on June 17, 2022Comments
-
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 about 10 yearsBecause the named resource isn't available via the CLASSPATH.
-
-
Withheld about 10 yearsThank you and that's exactly what I originally did. I put the file in both the
src/main/resources
and thewebapp/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 about 10 yearsCheck 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 about 10 yearsYes, 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 about 10 yearsThere is no single classpath. It all depends on the
ClassLoader
. It can delegate to its parentClassLoader
if it can't find it by itself, but I suspect thatJarClassLoader
(likeURLClassLoader
) doesn't delegate to its parent. You could use thegetPackages()
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 about 10 yearsI 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 about 10 yearsWell, packages are also paths. Classes in package com.foo.bar will be in com/foo/bar/.