Until java9 for adding external jar to classpath in runtime by programmatically everybody used:
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
method.invoke(sysloader, new Object[]{file.toURI().toURL()});
Now with java9 we have problem:
Exception in thread "main" java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader
URLClassLoader
doesn't work anymore in Java 9. What to do now under jdk9 for adding an external jar to the classpath in runtime programmatically?
The constructor will be called on JVM startup and the real system classloader will be passed, the main class will be loaded by the custom loader. To add jars just call ClassLoader. getSystemClassLoader() and cast it to your class.
In general, to include all of the JARs in a given directory, you can use the wildcard * (not *. jar ). The wildcard only matches JARs, not class files; to get all classes in a directory, just end the classpath entry at the directory name.
This can also be set in the Eclipse GUI by right-clicking on the project, selecting Properties->Java Build Path. Then select Add External Class Folder and enter the directory where you want to store your classes.
The JavaSE9 release notes read about the same :
The application class loader is no longer an instance of
java.net.URLClassLoader
(an implementation detail that was never specified in previous releases).Code that assumes that
ClassLoader::getSytemClassLoader
returns aURLClassLoader
object will need to be updated.Note that Java SE and the JDK do not provide an API for applications or libraries to dynamically augment the class path at run-time.
Additionally when an extended classpath is required, one can make use of
Class<?> clazz = Class.forName("nameofclass", true, new URLClassLoader(urlarrayofextrajarsordirs));
as suggested in this thread from Oracle. This comes with caveats:
java.util.ServiceLoader
uses the thread's ClassLoader context Thread.currentThread().setContextClassLoader(specialloader);
java.sql.DriverManager
does honors the calling class' ClassLoader, -not- the Thread's ClassLoader. Create Driver directly usingClass.forName("drivername", true, new URLClassLoader(urlarrayofextrajarsordirs).newInstance();
javax.activation
uses the thread's ClassLoader context (important for javax.mail).
Naman's answer is not a correct replacement for what you are looking for.
The correct way to add a jar to the classpath in Java 9 and above is to use Java Instrumentation's appendToSystemClassLoaderSearch(JarFile jarfile)
method.
First you will need to add your Agent class to your MANIFEST.MF
Launcher-Agent-Class: com.yourpackage.Agent
Then add your agent.
The example below will allow you to call Agent.addClassPath(File f)
to add a Jar to the classpath in both Java 8 & 9+
public class Agent {
private static Instrumentation inst = null;
// The JRE will call method before launching your main()
public static void agentmain(final String a, final Instrumentation inst) {
Agent.inst = inst;
}
public static boolean addClassPath(File f) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
try {
// If Java 9 or higher use Instrumentation
if (!(cl instanceof URLClassLoader)) {
inst.appendToSystemClassLoaderSearch(new JarFile(f));
return;
}
// If Java 8 or below fallback to old method
Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
m.setAccessible(true);
m.invoke(cl, (Object)f.toURI().toURL());
} catch (Throwable e) { e.printStackTrace(); }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With