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

(cl) NoClassDefFoundError in ClassLoader.defineClass1 does not mention name of initiating class

    XMLWordPrintable

Details

    • Enhancement
    • Resolution: Unresolved
    • P4
    • None
    • 6
    • core-libs

    Description

      Run:

      ---%<---
      import java.net.URL;
      import java.net.URLClassLoader;
      public class DemoNCDFE {
          public static void main(String[] args) throws Exception {
              new L().loadClass(A.class.getName()).newInstance();
          }
          private static class L extends URLClassLoader {
              L() {
                  super(new URL[] {DemoNCDFE.class.getProtectionDomain().getCodeSource().getLocation()},
                          DemoNCDFE.class.getClassLoader().getParent());
              }
              protected @Override Class<?> findClass(String name) throws ClassNotFoundException {
                  if (name.equals(C.class.getName())) {
                      throw new ClassNotFoundException(name);
                  }
                  return super.findClass(name);
              }
          }
          public static class A {
              public A() {new B();}
          }
          public static class B extends C {}
          public static class C {}
      }
      ---%<---

      You get something like this:

      ---%<---
      Exception in thread "main" java.lang.NoClassDefFoundError: DemoNCDFE$C
              at java.lang.ClassLoader.defineClass1(Native Method)
              at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
              at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
              at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
              at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
              at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
              at java.security.AccessController.doPrivileged(Native Method)
              at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
              at DemoNCDFE$L.findClass(DemoNCDFE.java:18)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
              at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
              at DemoNCDFE$A.<init>(DemoNCDFE.java:26)
              at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
              at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
              at java.lang.Class.newInstance0(Class.java:355)
              at java.lang.Class.newInstance(Class.java:308)
              at DemoNCDFE.main(DemoNCDFE.java:5)
      Caused by: java.lang.ClassNotFoundException: DemoNCDFE$C
              at DemoNCDFE$L.findClass(DemoNCDFE.java:15)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
              at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
              ... 20 more
      ---%<---

      While you can clearly see that the missing class was C, and with closer inspection you can see that an attempt was made to load C somewhere within the body of A's constructor, there is no indication of what the initiating class was - i.e. why C needed to be loaded at all.

      Of course in this trivial example it is easy to examine the source code and figure it out. In realistic cases, usually involving complex chains of class loaders, it is often a lot to work to infer what initiating class (here, B) triggered the CNFE (here on C). Knowing the initiating class is crucial, because this points you to the bad ClassLoader linkage.

      For example, in a modular system such as NetBeans or OSGi, normally class resolution is unlikely to result in NCDFE because the associated build system understands module dependencies and will not let you compile against an unexpressed dependency. However, if a class is included in a module in binary form (common when adding module metadata to a precompiled third-party library), it is necessary to ensure (perhaps manually) that all of its possible runtime dependencies will in fact be met. If you forget one, a NCDFE may be thrown. In the above example, if B and C had been from two external libraries, and the dependency from B -> C had been accidentally omitted, the developer would have a hard time identifying the real problem: after all, A had a correct dependency on B, but no apparent dependency on C, and nothing in the stack trace mentions B.

      This is especially irritating because the JVM clearly "knows" that it was the resolution of B which failed; yet this fact is not mentioned in the NCDFE detail message created by defineClass1.

      Originally reported as

      http://www.netbeans.org/nonav/issues/show_bug.cgi?id=145503

      Attachments

        Activity

          People

            Unassigned Unassigned
            jglick Jesse Glick (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Imported:
              Indexed: