package org.signalml.plugin.loader; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import org.apache.log4j.Logger; /** * A class loader that handles a single plugin (JAR file). * * The delegation model for loading classes is implemented in {@link #findClass(String)} * and features plugin dependencies. * * @author Stanislaw Findeisen (Eisenbits) */ public class PluginLoaderLo extends java.net.URLClassLoader { protected static final Logger log = Logger.getLogger(PluginLoaderLo.class); /*** * Plugin this loader is serving. */ private PluginHead pluginHead; /** * List of classes loaded from THIS JAR FILE (no parent plugins!). */ private HashSet<String> classNamesCanonical = new HashSet<String>(); /** * List of classes loaded from THIS JAR FILE (no parent plugins!). */ private HashSet<String> classNames = new HashSet<String>(); /** * All classes found by {@link #findClass(String)}. */ private HashMap<String,Class<?>> foundClasses = new HashMap<String,Class<?>>(); protected PluginLoaderLo(PluginHead head, ClassLoader parent) { super(new URL[] {head.getDescription().getJarFileURL()}, parent); this.pluginHead = head; } /** * Finds the class with the specified binary name. The algorithm features * plugin dependencies. * * <ol> * <li>If the requested class is in {@link #foundClasses the cache}, it is returned. * <li>If not, this method is called recursively on plugins this one depends on. * <li>If class is not found, this plugin JAR file is searched. * <ol> * * Each time the class is found, it is stored in {@link #foundClasses the cache}. */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clazz = null; synchronized (this) { clazz = foundClasses.get(name); } if (null != clazz) return clazz; // Class not found, search parent plugins... log.debug("searching parent plugins for class " + name); for (PluginHead ph : pluginHead.getDependencies()) { PluginLoaderLo pl = ph.getLoader(); try { clazz = pl.findClass(name); if (null != clazz) { storeFC(name, clazz); return clazz; } } catch (ClassNotFoundException e) { // no class in this parent, continue to the next } } // Lookup this JAR file URL: log.debug("checking in JAR for " + name); clazz = super.findClass(name); store(clazz, name); return clazz; } /** * Returns true iff this loader has loaded a class of the given name from the underlying JAR file. * Classes retrieved from plugins this one depends on do not count. * * @param className * @return true iff this loader has loaded a class of the name className */ protected boolean hasLoaded(String className) { synchronized (this) { if (classNamesCanonical.contains(className)) return true; if (classNames.contains(className)) return true; } return false; } private void storeFC(String name, Class<?> clazz) { synchronized (this) { foundClasses.put(name, clazz); } } private void store(Class<?> clazz, String fcName) { String nameCan = clazz.getCanonicalName(); String name = clazz.getName(); log.debug("PlugIn.store: " + nameCan + " / " + this); synchronized (this) { classNamesCanonical.add(nameCan); classNames.add(name); foundClasses.put(fcName, clazz); } } protected PluginHead getPluginHead() { return pluginHead; } @Override public String toString() { return (Integer.toHexString(hashCode()) + "/" + (pluginHead.getDescription().getName())); } }