/** * Create a new plugin manager. * * @param pluginType Core type for a plugin. * @param pluginCategory Provides a category name to the plugin. Must not be null. * @param pluginSuffix Provides a suffix that will be trimmed off when converting to a plugin * name. Can be null. * @param classpath Custom class path to search for classes. */ public PluginManager( Class pluginType, String pluginCategory, String pluginSuffix, List<URL> classpath) { this.pluginCategory = pluginCategory; this.pluginSuffix = pluginSuffix; this.plugins = new ArrayList<Class<? extends PluginType>>(); this.interfaces = new ArrayList<Class<? extends PluginType>>(); Reflections reflections; if (classpath == null) { reflections = defaultReflections; } else { addClasspath(classpath); reflections = new Reflections( new ConfigurationBuilder().setUrls(classpath).setScanners(new SubTypesScanner())); } // Load all classes types filtering them by concrete. @SuppressWarnings("unchecked") Set<Class<? extends PluginType>> allTypes = reflections.getSubTypesOf(pluginType); for (Class<? extends PluginType> type : allTypes) { // The plugin manager does not support anonymous classes; to be a plugin, a class must have a // name. if (JVMUtils.isAnonymous(type)) continue; if (JVMUtils.isConcrete(type)) plugins.add(type); else interfaces.add(type); } pluginsByName = new TreeMap<String, Class<? extends PluginType>>(); for (Class<? extends PluginType> pluginClass : plugins) { String pluginName = getName(pluginClass); pluginsByName.put(pluginName, pluginClass); } // sort the plugins so the order of elements is deterministic sortPlugins(plugins); sortPlugins(interfaces); }
/** * Adds the URL to the system class loader classpath using reflection. HACK: Uses reflection to * modify the class path, and assumes loader is a URLClassLoader. * * @param urls URLs to add to the system class loader classpath. */ private static void addClasspath(List<URL> urls) { Set<URL> existing = JVMUtils.getClasspathURLs(); for (URL url : urls) { if (existing.contains(url)) continue; try { Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); if (!method.isAccessible()) method.setAccessible(true); method.invoke(ClassLoader.getSystemClassLoader(), url); } catch (Exception e) { throw new ReviewedStingException("Error adding url to the current classloader.", e); } } }
static { // turn off logging in the reflections library - they talk too much Reflections.log = null; Set<URL> classPathUrls = new LinkedHashSet<URL>(); URL cwd; try { cwd = new File(".").getAbsoluteFile().toURI().toURL(); } catch (MalformedURLException e) { throw new RuntimeException(e); } // NOTE: Reflections also scans directories for classes. // Meanwhile some of the jar MANIFEST.MF Bundle-ClassPath properties contain "." // Do NOT let reflections scan the CWD where it often picks up test classes when // they weren't explicitly in the classpath, for example the UninstantiableWalker for (URL url : JVMUtils.getClasspathURLs()) if (!url.equals(cwd)) classPathUrls.add(url); defaultReflections = new Reflections( new ConfigurationBuilder().setUrls(classPathUrls).setScanners(new SubTypesScanner())); }