76

Is it possible to add a file (not necessarily a jar file) to java classpath at runtime. Specifically, the file already is present in the classpath, what I want is whether I can add a modified copy of this file to the classpath.

Thanks,

3
  • Is there a way to remove a jar file? Or substitute it with a newer version? Commented Jan 22, 2015 at 16:10
  • Is this even a duplicate? The linked question is about jars, not just files Commented May 22, 2019 at 14:05
  • maybe through OSGi or alike. Commented Nov 5, 2024 at 9:57

7 Answers 7

48

You can only add folders or jar files to a class loader. So if you have a single class file, you need to put it into the appropriate folder structure first.

Here is a rather ugly hack that adds to the SystemClassLoader at runtime:

import java.io.IOException;
import java.io.File;
import java.net.URLClassLoader;
import java.net.URL;
import java.lang.reflect.Method;

public class ClassPathHacker {

  private static final Class[] parameters = new Class[]{URL.class};

  public static void addFile(String s) throws IOException {
    File f = new File(s);
    addFile(f);
  }//end method

  public static void addFile(File f) throws IOException {
    addURL(f.toURL());
  }//end method


  public static void addURL(URL u) throws IOException {

    URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
    Class sysclass = URLClassLoader.class;

    try {
      Method method = sysclass.getDeclaredMethod("addURL", parameters);
      method.setAccessible(true);
      method.invoke(sysloader, new Object[]{u});
    } catch (Throwable t) {
      t.printStackTrace();
      throw new IOException("Error, could not add URL to system classloader");
    }//end try catch

   }//end method

}//end class

The reflection is necessary to access the protected method addURL. This could fail if there is a SecurityManager.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the reply guys, I'm guessing we can also add a plain file (not a class or jar etc...) in the classpath, But how do I know which one would get picked up (the latest addition the the classpath or the old file)
Ah, that is a problem. Much of this is implementation dependent, so you should really not have to classes (or other resources) with the same name in the same classloader.
If you put them into separate classloaders, then there is a spec. Usually the parent classloader takes precendence, for webapps it is sometimes the other way around (but still well-defined). The bootloader always comes first.
This throws java.lang.ClassCastException: class jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to class java.net.URLClassLoader
Note for anyone who runs across this answer in the future. This code assumes that the loader returned by getSystemClassLoader() is an instance of URLClassLoader, which is no longer true as of Java 9.
|
44

Try this one on for size.

private static void addSoftwareLibrary(File file) throws Exception {
    Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
    method.setAccessible(true);
    method.invoke(ClassLoader.getSystemClassLoader(), new Object[]{file.toURI().toURL()});
}

This edits the system class loader to include the given library jar. It is pretty ugly, but it works.

4 Comments

Great answer. I would however edit it a little, making use of varargs to keep it even more simpler.
Works! I reduced it by removing the arrays, as the parameters are varargs (by now?) so no arrays needed anymore.
Note for anyone who runs across this answer in the future. This code assumes that the loader returned by getSystemClassLoader() is an instance of URLClassLoader, which is no longer true as of Java 9.
With Java 21 I get at ...getDeclaredMethod("addURL", ...: "java.lang.reflect.InaccessibleObjectException: Unable to make protected void java.net.URLClassLoader.addURL(java.net.URL) accessible: module java.base does not "opens java.net" to unnamed module @5700d6b1".
14

The way I have done this is by using my own class loader

URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
DynamicURLClassLoader dynalLoader = new DynamicURLClassLoader(urlClassLoader);

And create the following class:

public class DynamicURLClassLoader extends URLClassLoader {

    public DynamicURLClassLoader(URLClassLoader classLoader) {
        super(classLoader.getURLs());
    }

    @Override
    public void addURL(URL url) {
        super.addURL(url);
    }
}

Works without any reflection

3 Comments

This solution sounded good to me, but it does not work (maybe because I'm in a Tomcat webapp?). I get a "class not found" error when trying to call a method from a class contained in the dynamic loaded JAR. The other solutions do work, even in a webapp.
This doesn't work since you create a new classloader, the other classloaders still won't access the file you added to this classloader
Note for anyone who runs across this answer in the future. This code assumes that the loader returned by getSystemClassLoader() is an instance of URLClassLoader, which is no longer true as of Java 9.
6

You coud try java.net.URLClassloader with the url of the folder/jar where your updated class resides and use it instead of the default classloader when creating a new thread.

Comments

0

Yes I believe it's possible but you might have to implement your own classloader. I have never done it but that is the path I would probably look at.

Comments

-1

yes, you can. it will need to be in its package structure in a separate directory from the rest of your compiled code if you want to isolate it. you will then just put its base dir in the front of the classpath on the command line.

Comments

-2

My solution:

File jarToAdd = new File("/path/to/file");

new URLClassLoader(((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs()) {
    @Override
    public void addURL(URL url) {
         super.addURL(url);
    }
}.addURL(jarToAdd.toURI().toURL());

3 Comments

Please explain what your code does (in the answer itself, not the comments) in addition to providing it.
This doesn't work because the addURL method in URLClassLoader is protected. But you can write your own URLClassLoader which rewrites the method and the modifier should be accessible.
This adds the file to the temporary loader, not the system loader, without any persistent effect. Starting with Java 9, it will fail with an exception, due to the wrong assumption that getSystemClassLoader() returns an URLClassLoader.