39

I understand from What is the difference between Class.getResource() and ClassLoader.getResource()? and from own code, that

getClass().getResource("/path/image.png")

is identical to

getClass().getClassLoader().getResource("path/image.png")

The posting Cannot read an image in jar file shows an issue where using

getClass().getClassLoader().getResource("path/image.png")

in an executable jar file returns null, while

getClass().getResource("/path/image.png")

returns the correct URL.

Since Class.getResource() delegates to ClassLoader.getResource() after removing the leading slash, I would expect that these calls are identical, but obviously they are not in this case. Even when a special class loader is attached to the particular class, it should still be the same one for each call, again resulting in the same behavior.

So, the question is: are there any obvious circumstances under which the following code returns null for the first call but the proper URL for the second call?

package com.example;

import java.net.URL;

public class ResourceTest {

   public void run() {
      URL iconUrl1 = getClass().getClassLoader().getResource("path/image.png");
      System.out.println("ClassLoader.getResource(\"path/image.png\"): " + iconUrl1);

      URL iconUrl2 = getClass().getResource("/path/image.png");
      System.out.println("Class.getResource(\"/path/image.png\"): " + iconUrl2);
   }

   public static void main(String[] args) {
      ResourceTest app = new ResourceTest();
      app.run();
   }
}
Community
  • 1
  • 1
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
  • 1
    Have you reproduced this behavior or is this question just based on the one you linked? Try to provide an SSCCE, then people will be able to answer this question. – rolve Nov 07 '12 at 12:34
  • 1
    Unfortunately it is just based on the one I linked - I was not able to reproduce it. I would have definitely posted an SSCCE if I could :), my hope is to get some feedback through this question to be able to write code which reproduces such an issue. I already stepped into getClass().getResource() with the debugger, and I see the algorithm mentioned in the first link above, but I have no idea in which cases the behavior described in the linked question could occur. – Andreas Fester Nov 07 '12 at 12:47
  • Add a working example please. I looked in the java.lang classes source and javadoc for info, but I got the same information as you already mentioned. – gyorgyabraham Nov 08 '12 at 15:02
  • @gyabraham I have added a working sample. Any idea how to break it? (sounds like a strange question on SO ;) ) – Andreas Fester Nov 08 '12 at 15:34
  • Q: Any idea how to break it? A: Yes. If you want "null" for one but not the other, all you should have to do is put your "class" files in a different directory than your "classloader root". In other words, all you should have to do is introduce a package (which you should be doing anyway). My example does this on my machine: 1/2 the cases are "SUCCESS", and 1/2 are "NULL". I'm using JDK 1.6. I understand when you run *my same code*, you get *all* "SUCCESS" and no "NULL". Q: Is that correct? – paulsm4 Nov 10 '12 at 21:53
  • Q: Is that correct? A: No. See http://pastebin.com/eMgAiLNy - I get the same output like you. Remember: my question is all about lines 2 and 3 of your output. They both give SUCCESS, both in your and in my application. Which is the expected behavior. Q: Is it possible by some subtle mechanism that ClassLoader.getResource(subdir/readme.txt): returns null, while Class.getResource(/subdir/readme.txt) returns SUCCCESS? – Andreas Fester Nov 10 '12 at 22:14
  • @Andreas - So I guess the answer to your question is "No": 1) We're completely clear on the difference between ClassLoader.getResource() and Class.getResource(), correct? 2) Your sample output matches my sample output, for both "SUCCESS" and for "null", correct? 3) This matches what we'd *expect*, correct? – paulsm4 Nov 15 '12 at 18:14
  • @paulsm4 1) Yes. 2) Yes. 3) Yes. 4) Conclusion: Either we oversaw something, or something different caused the "solution" described in the linked question to work. I think we can not currently prove one or the other. If you like, phrase your points 1) 2) 3) as answer and I will happily accept it. – Andreas Fester Nov 15 '12 at 20:31
  • @AndreasFester I have a different scenario with regards to output. Both ClassLoader.getResource(subdir/readme.txt) and Class.getResource(/subdir/readme.txt) giving me SUCCESS when run in Eclipse. But both of them returning NULL when run from a executable jar on windows machine. Is there something I'm missing here? – Suresh Feb 08 '18 at 13:33

1 Answers1

43

I thought this question was already asked and answered!

getClass().getResource() searches relative to the .class file while getClass().getClassLoader().getResource() searches relative to the classpath root.

If there's an SSCCE here, I don't understand why it doesn't

1) Show the directory organization in the .jar, and...

2) Take package into consideration

Q: What (if anything) hasn't already been answered by What is the difference between Class.getResource() and ClassLoader.getResource()? (and the links it cites)?

=========================================================================

I'm still not sure what isn't clear, but this example might help:

/*
  SAMPLE OUTPUT:
  ClassLoader.getResource(/subdir/readme.txt): NULL
  Class.getResource(/subdir/readme.txt): SUCCESS

  ClassLoader.getResource(subdir/readme.txt): SUCCESS
  Class.getResource(subdir/readme.txt): NULL
 */
package com.so.resourcetest;

import java.net.URL;

public class ResourceTest {

    public static void main(String[] args) {
        ResourceTest app = new ResourceTest ();
    }

    public ResourceTest () {
        doClassLoaderGetResource ("/subdir/readme.txt");
        doClassGetResource ("/subdir/readme.txt");
        doClassLoaderGetResource ("subdir/readme.txt");
        doClassGetResource ("subdir/readme.txt");
    }

    private void doClassLoaderGetResource (String sPath) {
        URL url  = getClass().getClassLoader().getResource(sPath);
        if (url == null)
            System.out.println("ClassLoader.getResource(" + sPath + "): NULL");
        else
            System.out.println("ClassLoader.getResource(" + sPath + "): SUCCESS");
    }

    private void doClassGetResource (String sPath) {
        URL url  = getClass().getResource(sPath);
        if (url == null)
            System.out.println("Class.getResource(" + sPath + "): NULL");
        else
            System.out.println("Class.getResource(" + sPath + "): SUCCESS");
    }
}

Here's the corresponding directory tree. It happens to be an Eclipse project, but the directories are the same regardless if it's Eclipse, Netbeans ... or a .jar file:

C:.
├───.settings
├───bin
│   ├───com
│   │   └───so
│   │       └───resourcetest
│   └───subdir
└───src
    ├───com
    │   └───so
    │       └───resourcetest
    └───subdir

The file being opened is "subdir/readme.txt"


ADDENDUM 11/9/12:

Hi -

I copied your code verbatim from github, re-compiled and re-ran:

ClassLoader.getResource(/subdir/readme.txt): NULL
Class.getResource(/subdir/readme.txt): SUCCESS
ClassLoader.getResource(subdir/readme.txt): SUCCESS
Class.getResource(subdir/readme.txt): NULL

If that's not the output you're getting ... I'm baffled.

For whatever it's worth, I'm running:

  • Eclipse Indigo (it shouldn't matter)

  • Running inside the IDE (it shouldn't matter if it's filesystem or .jar, inside or outside an IDE)

  • My JRE is 1.6 (if anything, this is probably the biggie)

Sorry we haven't been able to resolve what I thought was a straightforward issue :(


ADDENDUM 11/21/12 (Andreas):

Since there was no recent activity on this question, I would like to summarize what we found:

  • From our common understanding, the answer to the above question is: "No, it is not possible that Class.getResource("/path/image.png") returns a valid URL, while ClassLoader.getResource("path/image.png") returns null":
    • We're completely clear on the difference between ClassLoader.getResource() and Class.getResource()
    • Our sample outputs match, for both "SUCCESS" and for "null"
    • The sample outputs match what we'd expect
    • Conclusion: Either we oversaw something, or something different caused the "solution" described in the linked question to work. I think we can not currently prove one or the other.
Community
  • 1
  • 1
paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • The question to which I still have not seen an answer is: How can `getClass().getResource("/path/image.png");` return a URL while `getClass().getClassLoader().getResource("path/image.png");` returns null in an executable jar file (assumed the same packaging). My understanding is "It's not possible, at least not in a vanilla standard JDK/JRE environment". In which case the OP of the originating (linked) question would have a completely different root issue. Otherwise, show me how to package my sample code to achieve this behaviour :) – Andreas Fester Nov 08 '12 at 19:39
  • Your sample is completely valid and is not different from my understanding and from the output of my sample. Still, the question is: is there any way to package this into an executable jar file, such that `Class.getResource(/subdir/readme.txt)` returns `SUCCESS`. but `ClassLoader.getResource(subdir/readme.txt)` returns `NULL`. – Andreas Fester Nov 09 '12 at 11:20
  • Yes, darn it!!!! That's exactly what my first example does!!!! Please look at the sample output - or run it yourself. In your own directory, or your own .jar - they're completely equivalent!!!! Please ... look ... *AGAIN*! And please remember: 1) "package" implies "directory paths", 2) the "absolute path" for the loader is one thing, 3) the "absolute path" for a "class" is, dependent on package, a *DIFFERENT* thing!!!! – paulsm4 Nov 09 '12 at 15:09
  • @Andreas - Q: Do you see how the example return "SUCCESS" for Class.getResource(/subdir/readme.txt) and "null" for ClassLoader.getResource(subdir/readme.txt)? Have you tried it yourself yet? With a .jar file? – paulsm4 Nov 09 '12 at 19:43
  • No, when I run your sample, ClassLoader.getResource(subdir/readme.txt) does not return "null", but SUCCESS, see http://pastebin.com/eMgAiLNy. For both, run from filesystem or executable jar. That is also what the comment in your sample code says. See https://github.com/afester/StackOverflow/tree/master/getResource for a complete runnable sample - the build shell script shows how the jar file is built and executed. – Andreas Fester Nov 09 '12 at 20:45
  • One is an absolute url, the other is a relative one, using your class as the base location. This is why everyone should have at least a fundamental understanding of web-age protocols. – Ajax May 03 '17 at 06:19
  • @paulsm4 Hi, I have a different scenario with regards to output. Both ClassLoader.getResource(subdir/readme.txt) and Class.getResource(/subdir/readme.txt) giving me SUCCESS when run in Eclipse. But both of them returning NULL when run from a executable jar on windows machine. Is there something I'm missing here? – Suresh Feb 08 '18 at 13:34
  • @mannedear One thing that I've noticed, when running from IDE, they are case-insensitive (both subDir/readMe.txt and subdir/readme.txt are same), however, when running from jar, they are case-sensitive (subDir/readMe.txt and subdir/readme.txt are different). – Mokarrom Hossain Dec 17 '18 at 15:15