Without knowing the specifics of your code & setup it's impossible to be sure, but it reminds me of a problem I encountered when trying my hand at custom classloaders.
The scenario is this:
- The boot loader (JVM proper/Tomcat/whichever) loads your code
- Your classloader loads your additions not available on the classpath of the above.
- Your code references those additions.
- Those additions are not available in the same namespace as the code loaded by the boot loader.
- Your code in the boot loader's namespace runs, attempts to reference code in your custom namespace and this is not visible from the boot loader's namespace. So at that point the JVM bails with the NoClassDefFound error.
The reason for this is that the classloader hierachy works in a single direction only: i.e. code from sub-namespaces (child classloaders) is not available (visible) in the broader parent namespace (parent classloader); and there is no good way to hack into this system.
Fortunately, you can define interfaces which are available in the parent namespace, then implement these in code only visible in the sub-namespace. Then, code that lives inside the parent namespace only is able to use this interface name for casting & method call purposes and thus access your sub-namespace component regardless.
In order for this to work, you must ensure that your custom classloader does not only load components that the parent loader has no access to (i.e. are outside of its classpath), but also those bits of your code which directly interfaces with these components and explicitly refer these symbols (typenames/methods etc.). Otherwise references to these components end up in the parent namespace (remember, the boot classloader loads all your own code by default) and you're back to the original problem.
You can do this by subverting the intended classloading delegation model. Normally, you would defer to the parent loader before attempting to load classes yourself. However, now you must check in advance that your code does not touch any of these components unvailable to the parent loader. The easiest way is probably to set up your code path in such a way that the classloader maintains a set of classnames which it is to load itself rather than allow the parent loader to load them.
You have to find a way to tell your custom classloader somehow, a type annotation on class declarations could be used for this. The idea here is that your classloader introspects classes loaded by the parent, and if it finds a particular custom annotation on the type name it will invoke methods on the annotation to get the name of the class symbols that it must not allow its parent loader to load.
Example:
@MyCustomAnnotation(implementation="my.custom.package.MyImpl")
public class MyFeatureProvider {
public MyFeature getFeature() { // return an instance of MyImpl here }
}
Note that because the class MyFeatureProvider
will be loaded before MyImpl
is, your classloader will know in advance about the annotation in MyFeatureProvider
so it will be able to override the default delegation model for MyImpl. Because the rest of your code only interacts with MyImpl
as an instance of MyFeature
the parent loader never need to balk at the sight of undefined symbols -- and the ClassNoDefFound error is solved.