38

In order to better understand how things works in Java, I'd like to know if I can dynamically add, at runtime, a directory to the class path.

For example, if I launch a .jar using "java -jar mycp.jar" and output the java.class.path property, I may get:

java.class.path: '.:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java'

Now can I modify this class path at runtime to add another directory? (for example before making the first call to a class using a .jar located in that directory I want to add).

Cedric Martin
  • 5,945
  • 4
  • 34
  • 66

3 Answers3

50

You can use the following method:

URLClassLoader.addURL(URL url)

But you'll need to do this with reflection since the method is protected:

public static void addPath(String s) throws Exception {
    File f = new File(s);
    URL u = f.toURL();
    URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
    Class urlClass = URLClassLoader.class;
    Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
    method.setAccessible(true);
    method.invoke(urlClassLoader, new Object[]{u});
}

See the Java Trail on Reflection. Especially the section Drawbacks of Reflection

Jonathan Spooner
  • 7,682
  • 2
  • 34
  • 41
  • this is not working form me.. i am not able to load class after this using Class.forName("com.mysql.jdbc.Driver"); – Awanish Kumar Apr 21 '17 at 11:08
  • There is a reason why `addURL` is not public OR private. This is a unnecessary brutal force method that shouldn't be used in production code. Java designers provided more than one way of doing what's needed. – Kashyap May 10 '19 at 14:23
  • 2
    seems like the cast to URLClassLoader is broken in Java 9 (https://community.oracle.com/thread/4011800 and others) – Maikefer Mar 11 '20 at 09:58
26

Update 2014: this is the code from the accepted answer, by Jonathan Spooner from 2011, slightly rewritten to have Eclipse's validators no longer create warnings (deprecation, rawtypes)

//need to do add path to Classpath with reflection since the URLClassLoader.addURL(URL url) method is protected:
public static void addPath(String s) throws Exception {
    File f = new File(s);
    URI u = f.toURI();
    URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
    Class<URLClassLoader> urlClass = URLClassLoader.class;
    Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
    method.setAccessible(true);
    method.invoke(urlClassLoader, new Object[]{u.toURL()});
}
knb
  • 9,138
  • 4
  • 58
  • 85
  • 5
    It would have been more stackoverflowish to edit the accepted answer, I think. No more duplicate answers, that's what we like at this site. – Zeemee Dec 02 '15 at 09:48
  • 2
    @Zeemee I hardly remember, maybe I thought this was an edge case. I simply decided to avoid the accepted answer too much; I did not want to go beyond author's intention. – knb Dec 02 '15 at 13:51
12

Yes, you can use URLClassLoader.. see example here. Doesn't use reflection.

-- edit --

Copying example from the link as suggested.

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Hashtable;
import javax.naming.*;

public class ChangeLoader {
  public static void main(String[] args) throws MalformedURLException {
    if (args.length != 1) {
      System.err.println("usage: java ChangeLoader extra_classpath_url");
      System.exit(-1);
    }

    String url = args[0];
    // Save class loader so that we can restore later
    ClassLoader prevCl = Thread.currentThread().getContextClassLoader();

    // Use prevCl as parent, so we extend instead of replace
    ClassLoader urlCl = 
          URLClassLoader.newInstance(new URL[] {new URL(url)}, prevCl);

    try {
      Thread.currentThread().setContextClassLoader(urlCl);
      Context ctx = new InitialContext();
      System.out.println(ctx.lookup("tutorial/report.txt"));
      ctx.close();
    } catch (NamingException e) {
      e.printStackTrace();
    } finally {
      // Restore old loader
      Thread.currentThread().setContextClassLoader(prevCl);
    }
  } // main()
}
Kashyap
  • 15,354
  • 13
  • 64
  • 103
  • The example there are good and they avoid using reflection. But maybe you can copy them here - in case the link gets broken? – Alexey Grigorev Oct 23 '15 at 12:18
  • 3
    It is great that this avoids reflection but it comes at a price: You are not adding a path to your process classpath but creating a new class loader which includes the new path and use it within a concrete thread, in this case, your current thread. That means that any path added this way will be only noted on those thread with explicitly modified class loaders. – Pablo Francisco Pérez Hidalgo Apr 07 '16 at 06:31
  • 1
    @PabloFranciscoPérezHidalgo which is, of course, the way it should be used - you're meant to nest your classloaders so they have a defined scope (what you do in application servers, osgi etc), not mutate your system classloader. – eis Sep 07 '17 at 05:29
  • @eis Sure, but the OP was curious as to whether it was possible to add paths to the current process classpath. – Pablo Francisco Pérez Hidalgo Sep 07 '17 at 06:54
  • @PabloFranciscoPérezHidalgo I don't think that's what OP intended. Anyway, I don't quite agree with the brut force method of reflection especially when it's not needed because Java folks provided ways to do it. – Kashyap May 10 '19 at 14:27