This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
I have spent some time in the past couple of days helping a platform developer on my team sort out some classloader issues related to a platform application. I have a suggestion for making it easier to identify and solve these problems in the future. He was attempting to integrate a servlet engine (Jetty) into the NetBeans Platform and received a ClassNotFoundException at runtime: java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet at java.net.URLClassLoader$1.run(URLClassLoader.java:200) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:244) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) Caused: java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at org.netbeans.JarClassLoader.doLoadClass(JarClassLoader.java:243) at org.netbeans.ProxyClassLoader.selfLoadClass(ProxyClassLoader.java:254) at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:228) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) at com.mycompany.myapp.mymodule.SomeClass.init(SomeClass.java:59) As you can see, it is not an easy stack trace to follow. And while it clearly tells him what class cannot be found (javax.servlet.http.HttpServlet), it does not identify the class or module which is attempting to load it. In this case, the com.mycompany.myapp.mymodule.SomeClass (obviously not the real name) class is not the one which was trying to load the HttpServlet class, but rather the one which was attempting to start Jetty (a library wrapper module), and Jetty was trying to load HttpServlet. I've truncated the stacktrace slightly here, but nowhere in the original version is any Jetty class mentioned. The solution was to add a dependency from the Jetty module to the Servlet API module, but this was not immediately obivous as nowhere in the stacktrace does it mention the Jetty module or the class in that module which tried to load HttpServlet. So I am suggesting an improvement to the module class loader which is that the ClassNotFoundException message be enhanced to identify the actual caller, perhaps using code like this: StackTraceElement[] trace = Thread.currentThread().getStackTrace(); StackTraceElement elem = trace[2]; String caller = elem.getClassName() + "#" + elem.getMethodName(); String message = "ClassNotFoundException trying to load " + name + ", possibly from " + caller; Something like this would have saved a lot of time trying to debug the problem.
This is probably unfixable in NB. There are already various RFEs open for the JRE to produce more useful NCDFE's which actually tell you what was going on when class loading fails. The NB module class loaders already give detailed messages for CNFE, but the message you see is from URLClassLoader, not NB. I think your suggested fix is off target. The JRE is already printing the full stack trace; Jetty code is really not in the stack at all. It is the VM which is invoking the class loading which fails here, as part of resolving another class, and the VM simply does not expose any information about why it is trying to load the visible class. If you attach a minimal, self-contained, reproducible test case I can at least take a look and try to see if there is any way NB can make more information visible to the developer. BTW if your Jetty module was missing a dep on the Servlet API, you should have been able to see this without even running the app: <verify-class-linkage>, run during the build of a module, prints warnings about classes in the module which would not be resolvable if loaded at runtime, according to the module's declared dependencies.
I agree with the assertion about my suggested fix being off target... It was the first thing that came to mind. I have just created a minimal, self-contained, reproducible test case which I will attach shortly. One key thing to note is that one could probably only encounter this problem when library modules are being used, as they are compiled outside of the NB build harness. Obviously the classpath used to originally compile them would have to include all referenced classes/interfaces, but those same dependencies may not be present at runtime. As you speculated, verify-class-linkage did warn about the missing class at build time. There are many such warnings shown and since most can be safely ignored (e.g. optional dependencies in third-party libraries), so they tend to get lost in the Ant output. But checking that is a good first step when debugging CNFE and so I will recommend this to the developers and also add an entry into the NB Developer FAQ.
Created attachment 68591 [details] Minimal example NB 6.5 suite to reproduce the problem described
ZIP attached with the suite that demonstrates the problem (although not quite as well as the original Jetty/Servlet API app). Module C defines an interface (SomeInterface) in a JAR file and Module B contains a JAR file which has an implementation of that interface (SomeImplementation), plus a class InvokerThingie which provides a static method that calls into SomeImplementation. Module A defines an action which invokes InvokerThingie. Steps to reproduce: 1. Unpack and run the suite 2. Click File menu 3. Select "Invoke Greeter..." item This throws a "java.lang.ClassNotFoundException: com.tomwheeler.example.c.publicapi.SomeInterface", but it is not immediately obvious what needs it. Upon further reflection, I think it would have been less obivous still what the problem was in the stacktrace had the InvokerThingie class been in a different module than the SomeImplementation class.
Created attachment 68592 [details] Suite which does a better job of illustrating the problem
I have attached a classloading_improvement_example_suite_v2.zip suite which does a better job of illustrating the problem, by adding an additional module B2 which contains the InvokerThingie class originally in Module B. Module B now only contains the SomeImplementation class in a JAR file. When the menu item is invoked, there is no mention of module B whatsoever. Therefore, aside from the verify-class- linkage warning from the build, there is no indication that the solution is to add a dependency from Module B onto Module C. Oddly, when invoked a second time during the same session, the stacktrace is completely different (it's a NoClassDefFoundError rather than a CNFE). It references the implementation class as unavailable rather than the interface. This is similar to the behavior I noticed while trying to debug the Jetty/Servlet API problem. This is similar
FYI I added a Wiki page: http://wiki.netbeans.org/DevFaqTroubleshootClassNotFound which describes the problem and how to interpret <verify-class-linkage> to aid in solving it.
The reason the second invocation gives NCDFE on the impl class is that the impl class failed to resolve correctly the first time, and so the class loader marks it as known to be unloadable (or something like this).
I found a place where I can insert the information about the trigger class. It works on your example, though I cannot promise it will work on other cases. Please try to verify in a realistic situation. core-main #5ee06cf4edfe
I filed a request for the JVM to do the right thing to begin with.
Nice work, Jesse. I built a local copy of the platform with this change and was able to verify not only my demonstration suite but also on the original application that prompted me to file the issue. In both cases, the error message was much more clear and would help a developer realize which dependencies need to be added. Thanks again and I am going to go vote for the JDK issue you filed.
Well, I was going to vote for the JDK bug but could not find it. There is no JDK bug #6747450 at bugs.sun.com and the most recent one I found doing a search for 'jglick' was an unrelated issue from September 3, 2008.
bugs.sun.com receives reports from the internal tracker in batch mode. It will likely appear within a few days. Yes I hate it too and hope that the OpenJDK project switches to a publicly visible (at least) tracking system soon.
Integrated into 'main-golden', will be available in build *200809120201* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress) Changeset: http://hg.netbeans.org/main/rev/5ee06cf4edfe User: Jesse Glick <jglick@netbeans.org> Log: #145503: improve diagnostics for NCDFE.