Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6273389

Request more diagnostic info from NoClassDefFoundError

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Duplicate
    • Icon: P4 P4
    • 7-pool
    • 5.0, 6
    • hotspot
    • x86
    • linux

      Would like to see more helpful diagnostic info printed in the detail message of a NoClassDefFoundError when it is thrown in the normal circumstances: a class could not resolve a dependency. Consider the following demo:

      ---%<---
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.net.URL;
      import java.net.URLClassLoader;
      import java.security.CodeSource;
      import java.security.cert.Certificate;
      public class TestNCDFE {
          public static void main(String[] args) throws Exception {
              String c1 = C1.class.getName();
              String c2 = C2.class.getName();
              URL u1 = new URL("http://nowhere.net/one.jar");
              URL u2 = new URL("http://nowhere.net/two.jar");
              ClassLoader bootstrap = ClassLoader.getSystemClassLoader().getParent();
              ClassLoader l1 = new TestLoader(u1, c1, bootstrap);
              ClassLoader l2 = new TestLoader(u2, c2, l1);
              System.out.println("We can load C2 OK: " + l2.loadClass(c2).newInstance());
              System.out.println("But should get NCDFE on C1: " + l2.loadClass(c1).newInstance());
          }
          private static final class TestLoader extends URLClassLoader {
              private final URL dummy;
              private final String loadable;
              public TestLoader(URL dummy, String loadable, ClassLoader parent) {
                  super(new URL[] {dummy}, parent);
                  this.dummy = dummy;
                  this.loadable = loadable;
              }
              protected Class findClass(String name) throws ClassNotFoundException {
                  if (name.equals(loadable)) {
                      try {
                          InputStream is = TestNCDFE.class.getClassLoader().getResourceAsStream(name.replace('.', '/') + ".class");
                          ByteArrayOutputStream baos = new ByteArrayOutputStream();
                          byte[] buf = new byte[4096];
                          int i;
                          while ((i = is.read(buf)) != -1) {
                              baos.write(buf, 0, i);
                          }
                          byte[] data = baos.toByteArray();
                          return defineClass(name, data, 0, data.length, new CodeSource(dummy, (Certificate[]) null));
                      } catch (IOException e) {
                          throw new ClassNotFoundException(name, e);
                      }
                  } else {
                      throw new ClassNotFoundException(name);
                  }
              }
              public String toString() {
                  return super.toString() + "[" + dummy + "]->" + getParent();
              }
          }
          public static final class C1 {
              public C1() {
                  new C2();
              }
          }
          public static final class C2 {}
      }
      ---%<---

      Here the dependencies are backwards: C1 is trying to refer to C2, yet C1's loader cannot access C2's loader.

      If you try to run it in JDK 6.0, you get

      ---%<---
      We can load C2 OK: TestNCDFE$C2@d9f9c3
      Exception in thread "main" java.lang.NoClassDefFoundError: TestNCDFE$C2
              at TestNCDFE$C1.<init>(TestNCDFE.java:50)
              at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
              at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
              at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
              at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
              at java.lang.Class.newInstance0(Class.java:350)
              at java.lang.Class.newInstance(Class.java:303)
              at TestNCDFE.main(TestNCDFE.java:18)
      ---%<---

      You can see the class that could not be found (C2) and in this example you can also guess at which class was referring to it (C1). What you don't see is an explanation for why there was a problem, because there is no information about what was loaded from where. Now if you get a NCDFE, either

      1. You really deleted the referred-to .class file, which the JRE can't hope to help you with, but this is pretty easy to figure out - it's not there.

      2. The referred-to .class file exists, but in the wrong loader. Sometimes this is quite subtle, e.g. in this example I tried to load both classes from l2, so it takes some thought to realize that the actual loader for c1 was l1 and therefore c1 cannot find c2 even though c2 is accessible from l2. You can figure out what is going on if you understand class loaders a bit, but you need a clue - and the current JRE does not give you the critical information.
      ###@###.### 2005-05-19 22:55:57 GMT
      Would also appreciate having ClassCastException's include more details. Currently if you mix up your class loaders incorrectly it is possible to have code that says

      SomeClass o = (SomeClass) something();

      fail with

      java.lang.ClassCastException: pkg.SomeClass

      which is mystifying since that looks to be the very class you were casting to. Whereas in fact the return value of something() was assignable to a different instance of SomeClass.class! For the particular case that a JVM cast operation results in a CCE where the actual type and the desired type have the same name (or more generally where the actual type is assignable to a class with the same name as the desired type), the detail message reported by the JVM should include a toString() representation of both ClassLoader's, e.g.

      java.lang.ClassCastException: pkg.SomeClass from java.net.URLClassLoader@123[file:/a.jar] is not assignable to pkg.SomeClass from java.net.URLClassLoader@456[file:/b.jar]

            acorn Karen Kinnear (Inactive)
            jglick Jesse Glick (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: