Adding jars at runtime

Here’s a class I came across while trying to solve a problem I had recently. Its a ‘hack’ so to speak, but it does work well. I have an app that uses the Comm API with java. The issue I had was the nusance of installing the app. You had to copy a few files to the jre and then set some classpath stuff. Not a big deal per se, but annoying, especially when I wanted a simple drag and drop solution.

This class helped provide that, by allowing me to add the comm.jar at runtime without having to make a batch file or modify the environment variables.


import java.io.*;
import java.net.*;
import java.lang.reflect.*;

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);
      }
      
      public static void addFile(File f) throws IOException {
            addURL(f.toURL());
      }

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

      }
}

I don’t take credit for this, I came across it at java.net on the forums.

Regards,
Dr. A>

very handy! Great for loading plugins and maps bundled as .jar files on the fly.

Will.

Wheres the difference to creating a new URLClassLoader?

It appears that this is acutally dropping the new URL into the system class loader which means I suppose you can pick up the new classes without having to use the new classloader anywhere else?

Kev

Looking at it tho, a far prettier solution would probably be (just typed, no compiler handy):



public class MySystemClassLoader {
     private static MySystemClassLoader single;

     public static MySystemClassLoader get() {
          return single;
     }

     private URLClassLoader loader;

     public MySystemClassLoader(ClassLoader loader) {
           super(new URL[0],loader);
 
           single = this;
     }

     public void addURL(URL u) {
          loader.addURL(u);
     }
}

public class Hook {
  public static void main(String argv[]) {
        MySystemClassLoader.get().addURL(new URL("whatever"));
  }
}
    

EDIT: actually you’d need to set the property “java.system.class.loader” to “MySystemClassLoader” outside, i.e. java -Djava.system.class.loader=MySystemClassLoader

Reference for why I think this should work: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html#getSystemClassLoader()

This way there’s no reflection involved or naughty assumptions about what the system class loader will or won’t be implemented as.

Anyone tell me if this strategy would work?

Kev

Thank you for this post!

I’ve been fighting with getting JavaFX apps to run from a JAR and this hack works perfectly. I added a few modifications so it is easier to use for loading JAR’s at runtime. My problem was that I need JavaFX, MySQL, and Java EE support, but I can’t expect the end user to play with their configuration settings to get the application to run.

As for the other responses there is a HUGE difference between this method and creating a new class loader. My first attempt was to load everything via class loaders but JavaFX will not work properly when loaded from a class loader since classes built by different loaders aren’t compatible.

Using this method I can search the users computer for their installation of JavaFX in the standard install folders. For the MySQL driver I include it inside the jar (jar within a jar). I save the mysql driver to the users home directory. After that loading it with the above method is trivial!