// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import org.openstreetmap.josm.tools.Logging;
/**
* Class loader for JOSM plugins.
*
* In addition to the classes in the plugin jar file, it loads classes of required
* plugins. The JOSM core classes should be provided by the parent class loader.
* @since 12322
*/
public class PluginClassLoader extends URLClassLoader {
private final Collection dependencies;
static {
ClassLoader.registerAsParallelCapable();
}
/**
* Create a new PluginClassLoader.
* @param urls URLs of the plugin jar file (and extra libraries)
* @param parent the parent class loader (for JOSM core classes)
* @param dependencies class loaders of required plugin; can be null
*/
public PluginClassLoader(URL[] urls, ClassLoader parent, Collection dependencies) {
super(urls, parent);
this.dependencies = dependencies == null ? new ArrayList<>() : new ArrayList<>(dependencies);
}
/**
* Add class loader of a required plugin.
* This plugin will have access to the classes of the dependent plugin
* @param dependency the class loader of the required plugin
* @return {@code true} if the collection of dependencies changed as a result of the call
* @since 12867
*/
public boolean addDependency(PluginClassLoader dependency) {
// Add dependency only if not already present (directly or transitively through another one)
boolean result = !dependencies.contains(Objects.requireNonNull(dependency, "dependency"))
&& !dependencies.stream().anyMatch(pcl -> pcl.dependencies.contains(dependency))
&& dependencies.add(dependency);
if (result) {
// Now, remove top-level single dependencies, which would be children of the added one
dependencies.removeIf(pcl -> pcl.dependencies.isEmpty() && dependency.dependencies.contains(pcl));
}
return result;
}
@Override
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class> result = findLoadedClass(name);
if (result == null) {
for (PluginClassLoader dep : dependencies) {
try {
result = dep.loadClass(name, resolve);
if (result != null) {
return result;
}
} catch (ClassNotFoundException e) {
// do nothing
Logging.trace("Plugin class not found in {0}: {1}", dep, e.getMessage());
}
}
result = super.loadClass(name, resolve);
}
if (result != null) {
return result;
}
throw new ClassNotFoundException(name);
}
@Override
public String toString() {
return "PluginClassLoader [urls=" + Arrays.toString(getURLs()) +
(dependencies.isEmpty() ? "" : ", dependencies=" + dependencies) + ']';
}
}