During the implementation of a feature of FDT we face the problem to load a jar at runtime. You can find many posts about how to do it. Basicly you use a URLClassLoader, tells him which jars to load and use reflection to instanciate the classes. But a problem arise if you load multiple jars and one of the jars try to access classes from an other jar. In this case you get ClassNotFoundExceptions, even if the same class loader is used to load the jars.
Finaly we find a blog post that solves this problem (thank you Jens!). The trick is to define your own classloader and override “loadClass” such that it first searches locally for the classes in the jars and then delegates to the parent class loader.
To illustrate this here is a small example. We define a main program and a plugin for this program. The plugin uses a class from an other jar. Both, the plugin and the extra jar are loaded at runtime.
The main program constist of a class Main, an interface IPlugin and a custom class loader PluginClassLoader. All are packaged in an main.jar.
-
package test;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class Main {
private PluginClassLoader urlLoader;
public static void main(String[] args) {
new Main();
}
public Main() {
try {
String[] stringURLs = new String[] {
"C:/Test/extra.jar",
"C:/Test/plugin.jar" };
URL[] urls = new URL[stringURLs.length];
for (int n = 0; n < stringURLs.length; n++)
urls[n] = new File(stringURLs[n]).toURL();
urlLoader = new PluginClassLoader(urls,
Main.class.getClassLoader());
IPlugin fcsh = (IPlugin) urlLoader.loadClass(
"plugin.ConcretePlugin").newInstance();
fcsh.perform();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
-
package test;
import java.net.URL;
import java.net.URLClassLoader;
public class PluginClassLoader extends URLClassLoader {
public PluginClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public Class<?> loadClass(String name)
throws ClassNotFoundException {
Class loadedClass = findLoadedClass(name);
if (loadedClass == null) {
try {
loadedClass = findClass(name);
} catch (ClassNotFoundException e) {
// Swallow exception
//does not exist locally
}
if (loadedClass == null) {
loadedClass = super.loadClass(name);
}
}
return loadedClass;
}
}
The plugin constist of a class ConcretePlugin, and is packaged as plugin.jar. On its classpath is the main.jar and the extra.jar.
The extra.jar contains the class Extra.
Now you are able to replace the extra.jar with an other version of that jar and load this at runtime. Hope this will save some java developers some time.