Dynamic ClassLoader

I have a great Java desktop application and I want other developers to develop plugins. Plugins will be placed in the specified directory. They will not be on the way to the class at startup. I will download and deploy them at runtime.

The complication is that some plugins will have dependencies on each other, as well as the main application. Therefore, I cannot load each plugin / jar into my own URLClassLoader. Therefore, I want to load all the plugins into 1 URLClassLoader. In addition, some plugins may not be initialized for various reasons. And I only want ClassLoader at the end of the day, who knows about successfully loaded plugins. The reasons are rather strange and are related to some legacies that use reflection to create classes. This should fail if the plugin is not initialized for classes defined inside the plugin flag that failed.

Without this requirement, the solution would be:

  • Build jar urls and create ClassLoader based on them
  • Try to initialize the plugin class from each jar (defined in config in the manifest)

ClassLoader will now be transferred to the legacy system to use for reflection. Nevertheless, I understand that he will still be able to create instances of classes from plugins whose plugin could not be initialized (since the bank will still be in the URL [] of the Loader class). Therefore, this violates my claim above.

The only solution I came across was to create a custom URLClassLoader as follows (just to allow access to findClass ()):

public class CustomURLClassLoader extends URLClassLoader { public CustomURLClassLoader(final URL[] urls, final ClassLoader parent) { super(urls, parent); } @Override protected Class<?> findClass(final String name) throws ClassNotFoundException { return super.findClass(name); } } 

And then I made another custom ClassLoader, which essentially knows about a few child classes of ClassLoaders:

 public class MultiURLClassLoader extends ClassLoader { private Set<CustomURLClassLoader> loaders = new HashSet<CustomURLClassLoader>(); public MultiURLClassLoader(final ClassLoader parent) { super(parent); } @Override protected Class<?> findClass(final String name) throws ClassNotFoundException { Iterator<CustomURLClassLoader> loadersIter = loaders.iterator(); boolean first = true; while (first || loadersIter.hasNext()) { try { if (first) { return super.findClass(name); } else { return loadersIter.next().findClass(name); } } catch (ClassNotFoundException e) { first = false; } } throw new ClassNotFoundException(name); } public void addClassLoader(final CustomURLClassLoader classLoader) { loaders.add(classLoader); } public void removeClassLoader(final CustomURLClassLoader classLoader) { loaders.remove(classLoader); } } 

Then my download plugin algorithm will be something like

 MultiURLClassLoader multiURLClassLoader = new MultiURLClassLoader(ClassLoader.getSystemClassLoader()); for (File pluginJar : new File("plugindir").listFiles()) { CustomURLClassLoader classLoader = null; try { URL pluginURL = pluginJar.toURI().toURL(); final URL[] pluginJarUrl = new URL[] { pluginURL }; classLoader = new CustomURLClassLoader(pluginJarUrl, multiURLClassLoader); multiURLClassLoader.addClassLoader(classLoader); Class<?> clazz = Class.forName("some.PluginClass", false, multiURLClassLoader); Constructor<?> ctor = clazz.getConstructor(); SomePluginInterface plugin = (SomePluginInterface)ctor1.newInstance(); plugin.initialise(); } catch (SomePluginInitialiseException e) { multiURLClassLoader.removeClassLoader(classLoader); } } 

Then I can transfer the multiURLClassLoader instance to the legacy system, and it can only find the classes (via reflection) whose plugin has been successfully loaded.

I did some basic testing and it seems to work the way I would like. But I would really like someone to think about whether this seems like a good idea or not? I've never played with ClassLoaders like this before, and I want to avoid penetrating too deeply too soon before it gets too late.

Thanks!

+4
source share
2 answers

The problem that I see is that if you do not know in advance which plug-in depends on the fact that it is very difficult to do something reasonable, debug problems, isolate broken or poorly designed plugins, etc.

Therefore, I would suggest another option: add a different field to each plugin manifest, which will say which other plugins it depends on. Perhaps just a list of other plug-in JAR modules that it should execute. (Key application classes will always be available.) I believe that this would make the design more robust and simplify many things.

Then you can choose from different designs, for example:

  • For each plugin, you can create a separate ClassLoader, which will load only the necessary JAR files. Probably the most reliable solution. But I see a drawback: plugins that act as dependencies for many others will be reloaded in different class loaders. It depends on the circumstances (number of plugins, JAR size, ...), if this can be a problem or not, it can even be an advantage.
  • You may have one large ClassLoader for all plugins as you suggest, but you can request it for the plugin classes in the order of their dependencies. Those that are not dependent on anything at first, and then those that are dependent on these first, etc. If any plugin class does not load / initialize, you can immediately discard all plugins that depend on it.
+4
source

Are you looking for something like OSGi ?

You could do something like Peter Pudlak, however you should take into account the fact that one of your decisions can create circular dependencies ...

+1
source

All Articles