I have a java project that uses a URLClassLoader to load classes from another jar file at runtime, like a plugin system.
Let me give you a simplified version of the problem: Let's say that in my main method I would create the ClassLoader, pass it getClass().getClassLoader() as the parent class loader and load my plugin class from the jar.
In the main method, I create an instance inst of the class and then pass it to a new thread. This new thread calls inst.getObject(), which is a method I defined.
Now, getObject() creates an instance of another class Builder in the jar via new - assuming the URLClassLoader would now load this class as well as it is the defining classloader of the current class. Here, a NoClassDefFoundError is thrown for Builder if the method is invoked from the thread, but not when invoked from the main method:
Exception in thread "Thread-0" java.lang.NoClassDefFoundError: testapp/testplugin/Builder
    at testapp.testplugin.Plugin.getObject(Plugin.java:88)
    at testapp.mainapp.TestInit$1.run(TestInit.java:90)
Caused by: java.lang.ClassNotFoundException: testapp.testplugin.Builder
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.net.FactoryURLClassLoader.loadClass(URLClassLoader.java:814)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 7 more
When I put System.out.println(getClass().getClassLoader().toString()) inside getObject(), the output is exactly the same whether I call the method from main or from the thread.
Any ideas as to why this happens? Here is some sample code:
Plugin (in plugin.jar):
package testapp.testplugin;
// Pluggable defines the getObject() method, common interface for all plugins
public class Plugin implements Pluggable{
  Builder build;
  public Plugin() {
    // set some fields
  }
  @Override
  public Object getObject()
  {
    // lazy initialisation for "build"
    if (build == null)
      build = new Builder(); ///// !NoClassDefFoundError! /////
    // make Builder assemble an object and return it
    return build.buildObject();
  }
}
Main application (in runnable app.jar):
package testapp.mainapp;
public class TestInit {
  public static void main(String[] args) throws Exception {
      // create URLClassLoader
      URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{new URL("testplugin.jar"},
          getClass().getClassLoader());
      // load plugin class
      Class<?> clazz = Class.forName("testapp.testplugin.Plugin", true, clazzLoader);
      Class<? extends Pluggable> subClazz = clazz.asSubclass(Pluggable.class);
      // instantiate plugin class using constructor (to avoid Class.newInstance())
      Constructor<? extends Pluggable> constr = subClazz.getConstructor();
      final Pluggable plugin = constr.newInstance();
      // create new thread and run getObject()
      Thread t = new Thread(){
           @Override
           public void run() {
                // something more sophisticated in the real application, but this is enough to reproduce the error
                System.out.println(plugin.getObject());
           }
      };
      t.start();
  }
}
My current workaround is to force-load the Builder class as soon as the plugin class is loaded:
public class Plugin {
    static
    {
        try
        {
            Class.forName("testapp.testplugin.Builder");
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
    }
[...]
}
 
    