215

Does anyone know how to programmaticly find out where the java classloader actually loads the class from?

I often work on large projects where the classpath gets very long and manual searching is not really an option. I recently had a problem where the classloader was loading an incorrect version of a class because it was on the classpath in two different places.

So how can I get the classloader to tell me where on disk the actual class file is coming from?

Edit: What about if the classloader actually fails to load the class due to a version mismatch (or something else), is there anyway we could find out what file its trying to read before it reads it?

Naman
  • 27,789
  • 26
  • 218
  • 353
luke
  • 14,518
  • 4
  • 46
  • 57

12 Answers12

209

Here's an example:

package foo;

public class Test
{
    public static void main(String[] args)
    {
        ClassLoader loader = Test.class.getClassLoader();
        System.out.println(loader.getResource("foo/Test.class"));
    }
}

This printed out:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 41
    To cut down on redundant typing, one can also use the shorter version: `Test.class.getResource("Test.class")`, which doesn't repeat the package name. – meriton Feb 01 '13 at 16:42
  • 1
    What if the class is compiled, e.g. from a .groovy file? – Ondra Žižka Jun 28 '13 at 00:58
  • 38
    @meriton: Or, to survive refactorinsgs: `Test.class.getResource(Test.class.getSimpleName() + ".class")` – leonbloy Jul 21 '13 at 16:50
  • 1
    For `BouncyCastleProvider` full package name is required however. – Pavel Vlasov Nov 07 '13 at 13:34
  • 3
    It is possible for `getClassLoader()` to return `null`. See [here](http://stackoverflow.com/a/19494116/823393) for an extension to this method to handle that. – OldCurmudgeon Jan 04 '14 at 10:47
  • @meriton `Class#getResource(String name)` delegates to the object's class loader, i.e `ClassLoader#getResource(String name)` method. – ahmehri Aug 08 '14 at 13:28
  • Also the `getResource` can return null. This code won't work every time. – Tomáš Zato Mar 07 '15 at 18:14
  • @TomášZato: It will work if the class is available to the classloader. I agree that if the OP needs to cope with the possibility of the class not being available, then they'd need to do a null check - that's an easy modification though. – Jon Skeet Mar 08 '15 at 12:39
  • 1
    I used this to load resources and this is where it failed. Specifically, it failed when I was running Maven test class - at that point, class in my project was trying to load resources from `/target/test-classes` instead of `/target/classes`. The problem was solved by using `new MyClass().getClass().getResource("...")` - or just `this.getClass ...` if in instantiated context. – Tomáš Zato Mar 08 '15 at 13:59
110

Another way to find out where a class is loaded from (without manipulating the source) is to start the Java VM with the option: -verbose:class

Ajinkya
  • 22,324
  • 33
  • 110
  • 161
jiriki
  • 1,664
  • 1
  • 12
  • 8
  • 6
    this worked very well, and doesn't have the problem of dealing with classes with null ClassLoader – lexicalscope Sep 09 '11 at 11:58
  • 2
    @ries If one doesn't need to do this programmatically, this is definitely the way to go, and it did solve my problem. However, the OP had asked specifically how to do this programmatically. – SantiBailors Feb 17 '17 at 16:09
97
getClass().getProtectionDomain().getCodeSource().getLocation();
Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
Dave DiFranco
  • 1,695
  • 10
  • 9
29

This is what we use:

public static String getClassResource(Class<?> klass) {
  return klass.getClassLoader().getResource(
     klass.getName().replace('.', '/') + ".class").toString();
}

This will work depending on the ClassLoader implementation: getClass().getProtectionDomain().getCodeSource().getLocation()

David Dossot
  • 33,403
  • 4
  • 38
  • 72
Jevgeni Kabanov
  • 2,632
  • 24
  • 23
21

Jon's version fails when the object's ClassLoader is registered as null which seems to imply that it was loaded by the Boot ClassLoader.

This method deals with that issue:

public static String whereFrom(Object o) {
  if ( o == null ) {
    return null;
  }
  Class<?> c = o.getClass();
  ClassLoader loader = c.getClassLoader();
  if ( loader == null ) {
    // Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
    loader = ClassLoader.getSystemClassLoader();
    while ( loader != null && loader.getParent() != null ) {
      loader = loader.getParent();
    }
  }
  if (loader != null) {
    String name = c.getCanonicalName();
    URL resource = loader.getResource(name.replace(".", "/") + ".class");
    if ( resource != null ) {
      return resource.toString();
    }
  }
  return "Unknown";
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
5

Edit just 1st line: Main.class

Class<?> c = Main.class;
String path = c.getResource(c.getSimpleName() + ".class").getPath().replace(c.getSimpleName() + ".class", "");

System.out.println(path);

Output:

/C:/Users/Test/bin/

Maybe bad style but works fine!

ecerer
  • 143
  • 1
  • 11
3

Typically, we don't what to use hardcoding. We can get className first, and then use ClassLoader to get the class URL.

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
  • 79
  • 1
  • 4
  • Needs to be: URL classUrl = MyClass.class.getClassLoader().getResource("/" + className); – Andrew Coates Jan 06 '20 at 15:04
  • MyClass.class is important part - getClass() can return Proxy! Then you can get name like MyClass$$EnhancerBySpringCGLIB$$a98db882.class, and null URL. – jalmasi Feb 13 '20 at 15:33
1

Take a look at this similar question. Tool to discover same class..

I think the most relevant obstacle is if you have a custom classloader ( loading from a db or ldap )

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
1

Simple way:

System.out.println(java.lang.String.class.getResource(String.class.getSimpleName()+".class"));

Out Example:

jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class

Or

String obj = "simple test"; System.out.println(obj.getClass().getResource(obj.getClass().getSimpleName()+".class"));

Out Example:

jar:file:/D:/Java/jdk1.8/jre/lib/rt.jar!/java/lang/String.class

seduardo
  • 704
  • 6
  • 6
0

This approach works for both files and jars:

Class clazz = Class.forName(nameOfClassYouWant);

URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class");
InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish
Adam
  • 16,808
  • 7
  • 52
  • 98
0

for unit test

If you need the location of a class called Coverage.java located in package com.acme.api

Class clazz = Coverage.class;
ClassLoader loader = clazz.getClassLoader();    
String coverageClazzCompiledAbsoluteTargetLocation = loader
.getResource(clazz.getCanonicalName().replace(".", File.separator) + ".class")
.getFile().replace(File.separatorChar + "$", "");
System.out.println(coverageClazzCompiledAbsoluteTargetLocation);

Result

/home/Joe/repositories/acme-api/target/test-classes/com/acme/api/Coverage.class

Note: Since java needs compilation, you cannot obtain the .java location (src/main/java/...). You need to compile .java into .class, so the location if you are using maven, will be inside of target folder

at runtime: JAR

If you build your app as jar, the location of class will be the jar location plus clazz package

at runtime: WAR

If you build your app as war, the location of class will be the war location (/bin folder in tomcat) plus clazz package

JRichardsz
  • 14,356
  • 6
  • 59
  • 94
-2

Assuming that you're working with a class named MyClass, the following should work:

MyClass.class.getClassLoader();

Whether or not you can get the on-disk location of the .class file is dependent on the classloader itself. For example, if you're using something like BCEL, a certain class may not even have an on-disk representation.

Daniel Spiewak
  • 54,515
  • 14
  • 108
  • 120
  • This returns the ClassLoader used for loading the class, isn't it? It does not find where the .class file is? – Koray Tugay Sep 26 '14 at 05:46
  • 1
    No it doesn't. The classloader can actually reffer to completely different class path - means it will be totally unable to reah the actual class location. – Tomáš Zato Mar 07 '15 at 18:16