Java isn't the most dynamic language around, but with some careful planning and flexibility you can make your programs a bit more dynamic by loading classes at runtime.
This might be good for when you want to make your application more extensible and allow certain modules within it to be replaced by simply dropping a jar that implements an interface in to a directory.
Or you could load user-written plugins at runtime. For example, this might be useful for adding functionality to a web app. Dropping in a plugin might let you change the way user authentication is performed or could add better logging.
The Code
Here is some code that will help you dynamically load a new class given the path to its jar file.
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class ExtensionLoader<C> {
public C LoadClass(String directory, String classpath, Class<C> parentClass) throws ClassNotFoundException {
File pluginsDir = new File(System.getProperty("user.dir") + directory);
for (File jar : pluginsDir.listFiles()) {
try {
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { jar.toURL() },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName(classpath, true, loader);
Class<? extends C> newClass = clazz.asSubclass(parentClass);
// Apparently its bad to use Class.newInstance, so we use
// newClass.getConstructor() instead
Constructor<? extends C> constructor = newClass.getConstructor();
return constructor.newInstance();
} catch (ClassNotFoundException e) {
// There might be multiple JARs in the directory,
// so keep looking
continue;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
throw new ClassNotFoundException("Class " + classpath
+ " wasn't found in directory " + System.getProperty("user.dir") + directory);
}
}
Using the Code:
ExtensionLoader<MyPlugin> loader = new ExtensionLoader<MyPlugin>();
somePlugin = loader.LoadClass("path/to/jar/file", "com.example.pluginXYZ", MyPlugin.class);
Some Notes
There are certainly better ways to make an application extensible (OSGi, Portlets, JPF, etc.), but sometimes you just need something simple like this.
Note that ExtensionLoader
only calls constructors with no arguments (bean-like classes). It might be more beneficial to return a Constructor<? extends C>
class and call it with the appropriate arguments. If this is the case, make sure you load the correct constructor from the newClass.getConstructor()
method.
WARNING: No security checks are done in this code, so make sure you trust the classes you're loading!