237

What's the difference between:

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)

and

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)

and

InputStream is = this.getClass().getResourceAsStream(fileName)

When are each one more appropriate to use than the others?

The file that I want to read is in the classpath as my class that reads the file. My class and the file are in the same jar and packaged up in an EAR file, and deployed in WebSphere 6.1.

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
zqudlyba
  • 2,393
  • 3
  • 15
  • 7

6 Answers6

308

There are subtle differences as to how the fileName you are passing is interpreted. Basically, you have 2 different methods: ClassLoader.getResourceAsStream() and Class.getResourceAsStream(). These two methods will locate the resource differently.

In Class.getResourceAsStream(path), the path is interpreted as a path local to the package of the class you are calling it from. For example calling, String.class.getResourceAsStream("myfile.txt") will look for a file in your classpath at the following location: "java/lang/myfile.txt". If your path starts with a /, then it will be considered an absolute path, and will start searching from the root of the classpath. So calling String.class.getResourceAsStream("/myfile.txt") will look at the following location in your class path ./myfile.txt.

ClassLoader.getResourceAsStream(path) will consider all paths to be absolute paths. So calling String.class.getClassLoader().getResourceAsStream("myfile.txt") and String.class.getClassLoader().getResourceAsStream("/myfile.txt") will both look for a file in your classpath at the following location: ./myfile.txt.

Everytime I mention a location in this post, it could be a location in your filesystem itself, or inside the corresponding jar file, depending on the Class and/or ClassLoader you are loading the resource from.

In your case, you are loading the class from an Application Server, so your should use Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName) instead of this.getClass().getClassLoader().getResourceAsStream(fileName). this.getClass().getResourceAsStream() will also work.

Read this article for more detailed information about that particular problem.


Warning for users of Tomcat 7 and below

One of the answers to this question states that my explanation seems to be incorrect for Tomcat 7. I've tried to look around to see why that would be the case.

So I've looked at the source code of Tomcat's WebAppClassLoader for several versions of Tomcat. The implementation of findResource(String name) (which is utimately responsible for producing the URL to the requested resource) is virtually identical in Tomcat 6 and Tomcat 7, but is different in Tomcat 8.

In versions 6 and 7, the implementation does not attempt to normalize the resource name. This means that in these versions, classLoader.getResourceAsStream("/resource.txt") may not produce the same result as classLoader.getResourceAsStream("resource.txt") event though it should (since that what the Javadoc specifies). [source code]

In version 8 though, the resource name is normalized to guarantee that the absolute version of the resource name is the one that is used. Therefore, in Tomcat 8, the two calls described above should always return the same result. [source code]

As a result, you have to be extra careful when using ClassLoader.getResourceAsStream() or Class.getResourceAsStream() on Tomcat versions earlier than 8. And you must also keep in mind that class.getResourceAsStream("/resource.txt") actually calls classLoader.getResourceAsStream("resource.txt") (the leading / is stripped).

LordOfThePigs
  • 11,050
  • 7
  • 45
  • 69
  • 2
    I'm pretty sure that `getClass().getResourceAsStream("/myfile.txt")` behaves differently from `getClassLoader().getResourceAsStream("/myfile.txt")`. – Rag May 13 '13 at 23:06
  • @BrianGordon: They don't behave differenty. Actually, the javadoc for [Class.getResourceAsStream(String)](http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResourceAsStream%28java.lang.String%29) says the following thing: "This method delegates to this object's class loader.", and then gives a bunch of rules on how it converts a relative path to an absolute path before delegating to the classloader. – LordOfThePigs Jun 18 '13 at 13:54
  • @LordOfThePigs Look at the actual source. Class.getResourceAsStream strips off the leading forward slash if you provide an absolute path. – Rag Jun 18 '13 at 16:39
  • 4
    @BrianGordon: Which makes it behave exactly the same as ClassLoader.getResourceAsStream() since the latter interprets all paths as absolute, whether they start with a leading slash or not. So as long as you path is absolute, both methods behave identically. If your path is relative, then the behavior is different. – LordOfThePigs Jun 21 '13 at 07:21
  • I couldn't find `getClassLoader()` of `String`, is it a mistake or need an extension? – AaA Aug 03 '19 at 11:52
  • `String.getResourceAsStream(..)` should probably be `String.class.getResourceAsStream(..)`. Similarly for `String.getClassLoader`. – Pshemo Aug 25 '20 at 21:29
  • Correct, I fixed that mistake in the answer. – LordOfThePigs Aug 26 '20 at 09:34
20

Use MyClass.class.getClassLoader().getResourceAsStream(path) to load resource associated with your code. Use MyClass.class.getResourceAsStream(path) as a shortcut, and for resources packaged within your class' package.

Use Thread.currentThread().getContextClassLoader().getResourceAsStream(path) to get resources that are part of client code, not tightly bounds to the calling code. You should be careful with this as the thread context class loader could be pointing at anything.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
6

Plain old Java on plain old Java 7 and no other dependencies demonstrates the difference...

I put file.txt in c:\temp\ and I put c:\temp\ on the classpath.

There is only one case where there is a difference between the two call.

class J {

 public static void main(String[] a) {
    // as "absolute"

    // ok   
    System.err.println(J.class.getResourceAsStream("/file.txt") != null); 

    // pop            
    System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null); 

    // as relative

    // ok
    System.err.println(J.class.getResourceAsStream("./file.txt") != null); 

    // ok
    System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null); 

    // no path

    // ok
    System.err.println(J.class.getResourceAsStream("file.txt") != null); 

   // ok
   System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null); 
  }
}
Kyr
  • 5,383
  • 2
  • 27
  • 22
John Lonergan
  • 305
  • 3
  • 6
4

All these answers around here, as well as the answers in this question, suggest that loading absolute URLs, like "/foo/bar.properties" treated the same by class.getResourceAsStream(String) and class.getClassLoader().getResourceAsStream(String). This is NOT the case, at least not in my Tomcat configuration/version (currently 7.0.40).

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

Sorry, I have absolutely no satisfying explanation, but I guess that tomcat does dirty tricks and his black magic with the classloaders and cause the difference. I always used class.getResourceAsStream(String) in the past and haven't had any problems.

PS: I also posted this over here

Community
  • 1
  • 1
Tim Büthe
  • 62,884
  • 17
  • 92
  • 129
  • Maybe tomcat decides to not respect the specification and, and doesn't treat all paths passed `ClassLoader.getResourceAsStream()` as absolute? This is plausible because as mentioned in some comments above, `Class.getResourceAsStream` actually calls getClassLoader().getResourceAsStream` but strips any leading slash. – LordOfThePigs Dec 10 '13 at 16:57
  • After checking in the source code of Java SE, I think I hold the answer: Both `Class.getResourceAsStream()` and `ClassLoader.getResourceAsStream()` ultimately end up calling `ClassLoader.findResource()` which is a protected method whose default implementation is empty, but whose javadoc explicitly states "Class loader implementations should override this method to specify where to find resources". I suspect tomcat's implementation of this particular method may be flawed. – LordOfThePigs Jul 05 '14 at 23:50
  • I've also compared the implementation of `WebAppClassLoader.findResource(String name)` in [Tomcat 7](http://svn.apache.org/repos/asf/tomcat/tc7.0.x/tags/TOMCAT_7_0_40/java/org/apache/catalina/loader/WebappClassLoader.java) with that of [Tomcat 8](http://svn.apache.org/repos/asf/tomcat/tc8.0.x/tags/TOMCAT_8_0_3/java/org/apache/catalina/loader/WebappClassLoader.java), and it appears that there are a key difference. Tomcat 8 explicitly normalizes the resource name by adding a leading `/` if it doesn't contain any, which makes all names absolute. Tomcat 7 doesn't. That's clearly a bug in Tomcat 7 – LordOfThePigs Jul 06 '14 at 00:00
  • I added a paragraph about that in my answer. – LordOfThePigs Jul 06 '14 at 00:23
3

After trying some ways to load the file with no success, I remembered I could use FileInputStream, which worked perfectly.

InputStream is = new FileInputStream("file.txt");

This is another way to read a file into an InputStream, it reads the file from the currently running folder.

António Almeida
  • 9,620
  • 8
  • 59
  • 66
  • It isn't a file, it is a resource. Answer is not correct. – user207421 Jul 03 '18 at 02:31
  • 1
    @EJP I end up in this SO answer, searching for ways to load a file, without knowing the difference between a file and a resource. I'm not going to delete my answer because it may help others. – António Almeida Jul 05 '18 at 18:28
-4

It Works , try out this :

InputStream in_s1 =   TopBrandData.class.getResourceAsStream("/assets/TopBrands.xml");
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245