Dynamic URL Class Loaders: A Simple Use Case

I recently discovered the usefulness of dynamically loading classes into a JVM and thought I would document the discovery.

Java has a class called a URL class loader (URLClassLoader). You can create one using a URL and install it as the current thread’s class loader. Any references to new classes will use the URL class loader first before looking at the parent class loader.

URL[] urls = ...
ClassLoader originalClassLoader = Thread.currentThread().getClassLoader();
ClassLoader newClassLoader = new URLClassLoader(urls, originalClassLoader);

try {    
    Thread.currentThread().setContextClassLoader(newClassLoader);
    // write code to load new classes
} finally {
    Thread.currentThread().setCLassLoader(originalClassLoader);
}

The example and the way I used this was for loading in JNDI initial contexts. See this tutorial for an example.
One can use this to load groups of classes together from a local jar file.
I found it useful to extend this a little with the ability to walk a file system searching for jar files to load using a single class loader.

public class JarSeekingURLClassLoader extends URLClassLoader {

    public JarSeekingURLClassLoader(File file, ClassLoader parent) throws MalformedURLException {
        super(makeUrls(file), parent);
    }

    private static URL[] makeUrls(File file) throws MalformedURLException {
        List<URL> urls = new ArrayList<URL>();
        urls.add(file.toURI().toURL());
        File[] jarFilesAndDirs = file.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return dir.isDirectory() || name.endsWith(".jar");
            }
        });
        if (jarFilesAndDirs != null) {
            for (File jarOrDir : jarFilesAndDirs) {
                if (jarOrDir.isDirectory()) {
                    urls.addAll(Arrays.asList(makeUrls(jarOrDir)));
                } else {
                    urls.add(jarOrDir.toURI().toURL());
                }
            }
        }
        return urls.toArray(new URL[urls.size()]);
    }

    public JarSeekingURLClassLoader(File file) throws MalformedURLException {
        super(makeUrls(file));
    }

    public JarSeekingURLClassLoader(File file, ClassLoader parent, URLStreamHandlerFactory factory) throws MalformedURLException {
        super(makeUrls(file), parent, factory);
    }
}

One can of course use these two patterns together to arrange to dynamically load a directory full of JAR files using a single class loader.

URL[] urls = ...
ClassLoader originalClassLoader = Thread.currentThread().getClassLoader();
ClassLoader newClassLoader = new JarSeekingURLClassLoader(new File(Config.getDynamicLibraryLocation());

try {    
    Thread.currentThread().setContextClassLoader(newClassLoader);
    // write code to load new classes
    Class.forName("com.dynamic.library.class");
} finally {
    Thread.currentThread().setCLassLoader(originalClassLoader);
}

This trick is handy if you need to simplify your compile time dependencies.

This is of course getting close to building a dynamic loading system like OSGi, but sometimes small concepts are more efficient.