2

I just came to realise that this will sometimes return null (print null in console):

package myproject;
public class A {
   public static void main(String[] args) throws Exception
   {
     System.out.println(A.class.getClassLoader().getResource("A.class"));
   }
}

Even though the ClassLoader is created from A.class it has nothing to do with it. It will load resources from the currently running classpath.

I realised this when I was running Maven test classes which run, by default, in project/target/test-classes while the normal classes, like A above, are in project/target/classes/.

I think this is very confusing. Why don't we get something like Class.getClassLoader to make it obvious that this is a global thing? Also, since ClassLoader is useless, what do developers use to load resources relative to their projects? If you include .jar dependency it works, resources included.

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • [FYI](http://stackoverflow.com/a/1921281/2749470) – Bhargav Modi Mar 07 '15 at 17:53
  • @BhargavModi this is not what I'm talking about. I didn't get ClassLoader null. I got null result from get resource. – Tomáš Zato Mar 07 '15 at 17:55
  • 2
    (speaking to the "since ClassLoader is useless..." point) Read the reference for [getResource](http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource(java.lang.String)) on the class object, which says that if the bootstrap classloader is used to load a class, the method will delegate to a static method on the `ClassLoader` class to find the resource. It's not useless in the strictest sense of the term, since under the hood you still maintain the ability to locate resources. The devil is sometimes in the details of how you _specify_ a resource location – Ryan J Mar 07 '15 at 17:56
  • @RyanJ thanks, this makes the whole thing more clear. But it poses another question, *how to create classloader that will not get redirected to ` ClassLoader.getSystemResource`?* Which would be mine 3rd consecutive question regarding classpath and resources in 30 minutes. So I hope somebody will post more complete answer that covers this. – Tomáš Zato Mar 07 '15 at 17:59
  • Anyone can create a custom ClassLoader that extends from the base `ClassLoader` class, that can be instantiated at runtime to locate classes that are not on the classpath. If you think of this in terms of how Java applications that use a "plugin" system to provide functionality, whereby you simply "drop-in" a jar file that provides some implementation not known at compile time. A custom loader is used to load these classes into memory and allow the use of the functionality after the program is run. – Ryan J Mar 07 '15 at 18:04
  • (cont) If the supplied jar file provides external resources as well, the custom class loader is used to locate those resources relative to the class path of the loader used to load those classes. Where the searching is defined is in the implementation of the custom class loader. The most basic behavior is either to search in the current "directory" of the referenced class, or if a leading `/` is present on the path, it will search the root of the classpath. – Ryan J Mar 07 '15 at 18:06
  • Any difference if you do getClass().getClassLoader() instead? Also, is the A-class in the default package? – aioobe Mar 07 '15 at 18:16
  • @aioobe No, the class isn't in the default package. Everything is in the insane empty directory tree chain enforced by Maven (`com/blah/bleh/clickonemore/...`). But I want a ClassLoader that will always look in the classpath the calling class was loaded from. I understand there may be some reflection complications, but at least partialy working solution would me nice. Not just random path generator I have now. – Tomáš Zato Mar 07 '15 at 18:19
  • @aioobe The important thing is that `.getResource(".")` returns completely different project path than the one class was loaded from. And this can be really easily reproduced, just create empty Maven project and two classes. – Tomáš Zato Mar 07 '15 at 18:25
  • 1
    Please answer this: Have you tried `getResource("myproject/A.class")`? If so, what is the result? – aioobe Mar 07 '15 at 18:48
  • @aioobe I have, it returned null too - don't forget it's still trying to look into `/target/test-classes` whereas my class is in `/target/classes/some/path`. What worked was your suggestion to use `this.getClass().getResource`. Apparently, there's a difference between static and not static usage. I still do not understand it though. – Tomáš Zato Mar 08 '15 at 14:01

2 Answers2

1

First of all: two different classes can be loaded by different class loaders. So, when you do:

ClassLoader forA = A.class.getClassLoader();
ClassLoader forB = B.class.getClassLoader();

it is very much possible that

if (forA.equals(forB)) {
  print equal
} else {
  print not equal
}

will print not equal!

Therefore your idea of using a single global Class.getClassLoader() breaks immediately: as there isn't a single entity that this method call could possibly return.

And for the other part - about access resources ... have a look at this SO question.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • And what does the non-global return? It returns the same value in all classes around the project. That's what I call a global thing. If all class loaders return the same thing, what's the point? – Tomáš Zato Mar 07 '15 at 17:56
  • But all classes does *not* necessarily return the same class loader. – aioobe Mar 07 '15 at 17:58
  • Behind this question I stand in front of real problem where my `ClassLoader` is returning useless crap. Your two sentenced answer didn't shift my view anywhere - I wonder if you really consider this a serious answer. I'm not saying that you're not right, documentation is clear, but your answer raises more questions than it anwers. And it doesn't even answer all the questions I asked in the original question. – Tomáš Zato Mar 07 '15 at 18:07
1

Each class is associated with the class loader that loaded the class. It makes perfect sense to let Class have an getClassLoader instance method.

It's nothing "wrong" with having multiple different objects return the same object through a get method.

It's like having, say person.getAddress(). All persons in a family may return the same address, but that doesn't mean it makes sense to have a static Person.getAddress.

aioobe
  • 413,195
  • 112
  • 811
  • 826
  • And what's the use of that association if the loaders seem to do the same thing? Note that I'm not saying there's no association, I'm asking *what is it* and *how to make use of it*. – Tomáš Zato Mar 07 '15 at 18:01
  • It's commonly used in security related code. Classes loaded from a user defined class loader typically have stricter security constraints than classes loaded by the bootstrap class loader. – aioobe Mar 07 '15 at 18:03
  • Ok, but in my project my neighbours family returns the same address as my own. That's **not OK anymore**. Obviously counter intuitive. My question includes the question asking how to do it properly when this intuitive solution sucks. – Tomáš Zato Mar 07 '15 at 18:10
  • I think you need to explain your use case a bit better. Your question is not really clear. *to make it obvious that this is a global thing?* -- It's not. *Also, since ClassLoader is useless* -- It's not. – aioobe Mar 07 '15 at 18:18
  • Does your question mean that you think it's expected for my code to fail and return null, instead of URL? (note that code I used is suggested by this 84-times upvoted answer: http://stackoverflow.com/q/227486/607407). Obviously, I want to know why should that code fail and return path for different project (which is what happens when running maven test classes). And I also asked how do I make a code that allways returns the proper classpath - or at least is as reliable as possible. – Tomáš Zato Mar 07 '15 at 18:24
  • The class loader you end up getting hold of is determined by the program you are running. If you're running your own program, using say `java -jar yourprogram.jar` then you have control over class loading and can load resources etc the usual way. If you invoke another program to run your code, such as `maven`, then strictly speaking all bets are off, because maven is free to decide what class loader to use for loading your classes and what capabilities this class loader should have... – aioobe Mar 09 '15 at 07:38
  • ... This is off course problematic as you've discovered. You should try to play along with maven. See for instance [this](http://devblog.virtage.com/2013/07/how-to-get-file-resource-from-maven-srctestresources-folder-in-junit-test/) or [this](http://stackoverflow.com/questions/2978471/how-to-use-maven-resources-also-as-test-resources). – aioobe Mar 09 '15 at 07:39