Add jar to classpath at runtime under java 9
Asked Answered
W

2

22

Until 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?

Wilhoit answered 31/12, 2017 at 10:38 Comment(6)
Though the release note read that the current JavaSE/JDK doesn't provide any such API to augment the classpath at run-time. Yet, the better you could elaborate on what you're ultimately trying to achieve, the more the community could help you.Margherita
ClassLoader.getSystemClassLoader() is not an URLClassLoader anymore but you can always create a new instance of URLClassLoader if you want.Quash
@Quash is correct, esp. if you can invoking the code in the JAR file via reflection anyway.Chryselephantine
Actually, with Java 9, you have two problems. Even if the ClassLoader were in fact a URLClassLoader, I doubt that attempt at reflection would be permitted in a modular application.Escamilla
I found out what "Gradle" had the problem with UrlClassLoader. Anybody know how they fixed it?Wilhoit
@EvgeniyEgorov Since you did not specify your exact use case, we're just guessing. But maybe you don't actually need to amend the existing class path and could instead create a new class loader for the new JAR? See this answer.Vesperal
M
11

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 a URLClassLoader 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 using Class.forName("drivername", true, new URLClassLoader(urlarrayofextrajarsordirs).newInstance();

  • javax.activation uses the thread's ClassLoader context (important for javax.mail).

Margherita answered 31/12, 2017 at 12:46 Comment(4)
Ok. What in return for the old?Wilhoit
@EvgeniyEgorov What exactly are you trying to achieve is the actual question. If you could elaborate on that, would be helpful.Margherita
I tried to find info about it in google search. Unfortunately, I found out no any info or docs.Wilhoit
@EvgeniyEgorov You might want to take a look at this thread as well and be definitive over what your requirement is.Margherita
A
3

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(); }
    }

}
Adventist answered 26/6, 2019 at 5:49 Comment(2)
jdk12. agentmain(..) wasn't called. MANIFEST.MF has the string with Launcher-Agent-Class.Wilhoit
Is any detailed maven exmple?Baroda

© 2022 - 2024 — McMap. All rights reserved.