I'm trying to dynamically load libraries instead of shading them into my JAR to reduce file size. It's a standalone application with a Bootstrap
class and a Main
class. The main class is responsible for loading libraries and calling the bootstrap class' execute(...)
method.
The downloading of the artifacts and loading of the library classes using Class.forName(class, true, libraryLoader)
works, but when I try to load the Bootstrap
class it can't find the class definitions for the library classes.
Here is how I'm currently trying to achieve this:
//////// Main.java /////////
/* ... public static void main(String[] args) { */
// download libraries
// these URLs reference the local JAR files downloaded
// i'm not going to get into detail on how i'm doing this right now, as this works
URL[] libraryUrls = ...;
// create parent loader for libraries
final ClassLoader rootLoader = Main.class.getClassLoader(); // app class loader
ClassLoader libraryLoader = new DelegatingClassLoader(new URLClassLoader(
libraryUrls,
rootLoader
), rootLoader);
/* this passes, so the libraries are successfully loaded onto the loaders ucp */
Class.forName("org.eclipse.jgit.transport.CredentialsProvider", true, libraryLoader);
// find URL for JAR of bootstrap class
ProtectionDomain domain = Main.class.getProtectionDomain();
CodeSource source = domain.getCodeSource();
URL file = source.getLocation();
// create child loader with our JAR file
ClassLoader appLoader = new DelegatingClassLoader(new URLClassLoader(
new URL[] { file }, libraryLoader), libraryLoader);
// load bootstrap class and execute
Class<?> bootstrap = Class.forName( // the error occurs here
"my.package.Bootstrap",
/* initialize */ true,
appLoader
);
bootstrap.getMethod("execute", String[].class)
.invoke(null, (Object) args);
/* } ... */
The DelegatingClassLoader
first tries to load a class through the delegate and if that fails delegates to the parent. Here is the exact source.
The specific error I get:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jgit/transport/CredentialsProvider
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at my.package.Main.main(Main.java:108)
// line 108 is the line where i call Class.forName("my.package.Bootstrap, ...)
I was expecting the references to library classes to be resolved correctly because the class loader of the Bootstrap
class should delegate to the library class loader, which I confirmed to have successfully 'loaded' the libraries.