/* * FindBugs - Find bugs in Java programs * Copyright (C) 2003-2005 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JOptionPane; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.util.ClassPathUtil; import edu.umd.cs.findbugs.util.JavaWebStart; /** * The DetectorFactoryCollection stores all of the DetectorFactory objects * used to create the Detectors which implement the various analyses. * It is a singleton class. * * @author David Hovemeyer * @see DetectorFactory */ public class DetectorFactoryCollection { private HashMap<String, Plugin> pluginByIdMap = new HashMap<String, Plugin>(); private Plugin corePlugin; private BugRanker adjustmentBugRanker; private ArrayList<DetectorFactory> factoryList = new ArrayList<DetectorFactory>(); private HashMap<String, DetectorFactory> factoriesByName = new HashMap<String, DetectorFactory>(); private HashMap<String, DetectorFactory> factoriesByDetectorClassName = new HashMap<String, DetectorFactory>(); private static DetectorFactoryCollection theInstance; private static final Object lock = new Object(); private boolean loaded = false; private URL[] pluginList; /** * Constructor. * loadPlugins() method must be called before * any detector factories can be accessed. */ DetectorFactoryCollection() { } /** * Set the list of plugins to load explicitly. * This must be done before the instance of DetectorFactoryCollection * is created. * * @param pluginList list of plugin Jar files to load */ public void setPluginList(URL[] pluginList) { if (loaded) throw new IllegalStateException(); this.pluginList = new URL[pluginList.length]; System.arraycopy(pluginList, 0, this.pluginList, 0, pluginList.length); } /** * Set the instance that should be returned as the singleton instance. * * @param instance the singleton instance to be set */ static void setInstance(DetectorFactoryCollection instance) { synchronized (lock) { if (theInstance != null) { throw new IllegalStateException(); } theInstance = instance; } } static void resetInstance(DetectorFactoryCollection instance) { synchronized (lock) { theInstance = instance; } } /** * Get the single instance of DetectorFactoryCollection. */ public static DetectorFactoryCollection instance() { synchronized (lock) { if (theInstance == null) { theInstance = new DetectorFactoryCollection(); } theInstance.ensureLoaded(); return theInstance; } } /** * Get the single instance of DetectorFactoryCollection. */ public static DetectorFactoryCollection rawInstance() { synchronized (lock) { if (theInstance == null) { theInstance = new DetectorFactoryCollection(); } return theInstance; } } /** * Return an Iterator over all available Plugin objects. */ public Iterator<Plugin> pluginIterator() { ensureLoaded(); return pluginByIdMap.values().iterator(); } /** * Return an Iterable of all available Plugin objects. */ public Iterable<Plugin> plugins() { ensureLoaded(); return pluginByIdMap.values(); } Plugin getCorePlugin() { ensureLoaded(); return corePlugin; } BugRanker getAdjustmentBugRanker() { ensureLoaded(); return adjustmentBugRanker; } /** * Get a Plugin by its unique id. * * @param pluginId the unique id * @return the Plugin with that id, or null if no such Plugin is found */ public Plugin getPluginById(String pluginId) { ensureLoaded(); return pluginByIdMap.get(pluginId); } /** * Return an Iterator over the DetectorFactory objects for all * registered Detectors. */ public Iterator<DetectorFactory> factoryIterator() { ensureLoaded(); return factoryList.iterator(); } /** * Look up a DetectorFactory by its short name. * * @param name the short name * @return the DetectorFactory, or null if there is no factory with that short name */ public DetectorFactory getFactory(String name) { ensureLoaded(); return factoriesByName.get(name); } /** * Look up a DetectorFactory by its class name. * * @param className the class name * @return the DetectoryFactory, or null if there is no factory with * that class name */ public DetectorFactory getFactoryByClassName(String className) { ensureLoaded(); return factoriesByDetectorClassName.get(className); } /** * Register a DetectorFactory. */ void registerDetector(DetectorFactory factory) { if (FindBugs.DEBUG) System.out.println("Registering detector: " + factory.getFullName()); String detectorName = factory.getShortName(); factoryList.add(factory); factoriesByName.put(detectorName, factory); factoriesByDetectorClassName.put(factory.getFullName(), factory); } private ArrayList<URL> determineInstalledPlugins() { ArrayList<URL> plugins = new ArrayList<URL>(); String homeDir = getFindBugsHome(); if (homeDir != null) { // // See what plugins are available in the ${findbugs.home}/plugin directory // File pluginDir = new File(new File(homeDir), "plugin"); File[] contentList = pluginDir.listFiles(); if (contentList == null) { return plugins; } for (File file : contentList) { if (file.getName().endsWith(".jar")) { try { plugins.add(file.toURI().toURL()); if (FindBugs.DEBUG) System.out.println("Found plugin: " + file.toString()); } catch (MalformedURLException e) { } } } } return plugins; } private static final Pattern[] findbugsJarNames = { Pattern.compile("findbugs\\.jar$"), }; /** * See if the location of ${findbugs.home} can be * inferred from the location of findbugs.jar in the classpath. * * @return inferred ${findbugs.home}, or null if * we can't figure it out */ private static String inferFindBugsHome() { for (Pattern jarNamePattern : findbugsJarNames) { String findbugsJarCodeBase = ClassPathUtil.findCodeBaseInClassPath(jarNamePattern, SystemProperties.getProperty("java.class.path")); if (findbugsJarCodeBase != null) { File findbugsJar = new File(findbugsJarCodeBase); File libDir = findbugsJar.getParentFile(); if (libDir.getName().equals("lib")) { String fbHome = libDir.getParent(); FindBugs.setHome(fbHome); return fbHome; } } } String classFilePath = FindBugs.class.getName().replaceAll("\\.", "/") + ".class"; URL resource = FindBugs.class.getClassLoader().getResource(classFilePath); if (resource != null && resource.getProtocol().equals("file")) { try { String classfile = URLDecoder.decode(resource.getPath(), Charset.defaultCharset().name()); Matcher m = Pattern.compile("(.*)/.*?/edu/umd.*").matcher(classfile); if (m.matches()) { String home = m.group(1); if (new File(home + "/etc/findbugs.xml").exists()) { FindBugs.setHome(home); return home; } } } catch (UnsupportedEncodingException e) { } } return null; } public static String getFindBugsHome() { String homeDir = FindBugs.getHome(); if (homeDir == null) { // Attempt to infer findbugs.home from the observed // location of findbugs.jar. homeDir = inferFindBugsHome(); } return homeDir; } public void ensureLoaded() { if (loaded) return; loadPlugins(); } /** * Directly set the collection of Plugins from which to load DetectorFactories. * May be called instead of loadPlugins(). * * @param plugins array of Plugins to register */ void setPlugins(Plugin[] plugins) { if (loaded) { throw new IllegalStateException(); } for (Plugin plugin : plugins) { pluginByIdMap.put(plugin.getPluginId(), plugin); } loaded = true; } /** * Load all plugins. If a setPluginList() has been called, then those * plugins are loaded. Otherwise, the "findbugs.home" property is checked to * determine where FindBugs is installed, and the plugin files are * dynamically loaded from the plugin directory. */ void loadPlugins() { if (loaded) throw new IllegalStateException("plugins already loaded"); // // Load the core plugin. // PluginLoader corePluginLoader = new PluginLoader(); try { loadPlugin(corePluginLoader); corePlugin = corePluginLoader.getPlugin(); } catch (PluginException e) { throw new IllegalStateException("Warning: could not load FindBugs core plugin: " + e.toString(), e); } URL u = PluginLoader.getCoreResource(BugRanker.ADJUST_FILENAME); try { adjustmentBugRanker = new BugRanker(u); } catch (IOException e1) { AnalysisContext.logError("Unable to parse " + u, e1); } List<URL> plugins; if (JavaWebStart.isRunningViaJavaWebstart()) { plugins = determineWebStartPlugins(); } else { plugins = determineInstalledPlugins(); } Set<Entry<Object, Object>> entrySet = SystemProperties.getAllProperties().entrySet(); for(Map.Entry<?,?> e : entrySet) { if (e.getKey() instanceof String && e.getValue() instanceof String && ((String)e.getKey()).startsWith("findbugs.plugin.")) { try { String value = (String) e.getValue(); if (value.startsWith("file:") && !value.endsWith(".jar") && !value.endsWith("/")) value += "/"; URL url = JavaWebStart.resolveRelativeToJnlpCodebase(value); plugins.add(url); } catch (MalformedURLException e1) { AnalysisContext.logError(String.format("Bad URL for plugin: %s=%s", e.getKey(), e.getValue()), e1); } } } if (!plugins.isEmpty() && JavaWebStart.isRunningViaJavaWebstart()) { // disable security manager; plugins cause problems // http://lopica.sourceforge.net/faq.html // URL policyUrl = Thread.currentThread().getContextClassLoader().getResource("my.java.policy"); // Policy.getPolicy().refresh(); System.setSecurityManager(null); } // // Load any discovered third-party plugins. // for (final URL url : plugins) { try { jawsDebugMessage("Loading plugin: " + url.toString()); PluginLoader pluginLoader = AccessController.doPrivileged(new PrivilegedExceptionAction<PluginLoader>() { public PluginLoader run() throws PluginException { return new PluginLoader(url, this.getClass().getClassLoader()); } }); loadPlugin(pluginLoader); } catch (PluginDoesntContainMetadataException e) { if (FindBugs.DEBUG) e.printStackTrace(); } catch (PluginException e) { jawsErrorMessage("Warning: could not load plugin " + url + ": " + e.toString()); if (FindBugs.DEBUG) e.printStackTrace(); } catch (PrivilegedActionException e) { jawsErrorMessage("Warning: could not load plugin " + url + ": " + e.toString()); if (FindBugs.DEBUG) e.printStackTrace(); } } setPluginList(plugins.toArray(new URL[plugins.size()])); loaded = true; } /** * @param plugins */ private List<URL> determineWebStartPlugins() { List<URL> plugins = new LinkedList<URL>(); URL pluginListProperties = PluginLoader.getCoreResource("pluginlist.properties"); if (pluginListProperties != null) { try { jawsDebugMessage(pluginListProperties.toString()); URL base = getUrlBase(pluginListProperties); BufferedReader in = new BufferedReader(new InputStreamReader(pluginListProperties.openStream())); while (true) { String plugin = in.readLine(); if (plugin == null) break; URL url = new URL(base, plugin); try { URLConnection connection = url.openConnection(); String contentType = connection.getContentType(); jawsDebugMessage("contentType : " + contentType); if (connection instanceof HttpURLConnection) ((HttpURLConnection)connection).disconnect(); plugins.add(url); } catch (Exception e) { jawsDebugMessage("error loading " + url + " : " + e.getMessage()); } } in.close(); } catch (Exception e) { jawsDebugMessage("error : " + e.getMessage()); } } return plugins; } /** * @param pluginListProperties * @return * @throws MalformedURLException */ private URL getUrlBase(URL pluginListProperties) throws MalformedURLException { String urlname = pluginListProperties.toString(); URL base = pluginListProperties; int pos = urlname.indexOf("!/"); if (pos >= 0 && urlname.startsWith("jar:")) { urlname = urlname.substring(4,pos); base = new URL(urlname); } return base; } final static boolean DEBUG_JAWS = SystemProperties.getBoolean("findbugs.jaws.debug"); /** * @param message */ public static void jawsDebugMessage(String message) { if (DEBUG_JAWS) JOptionPane.showMessageDialog(null, message); else if (FindBugs.DEBUG) System.err.println(message); } public static void jawsErrorMessage(String message) { if (DEBUG_JAWS) JOptionPane.showMessageDialog(null, message); else System.err.println(message); } private void loadPlugin(PluginLoader pluginLoader) throws PluginException { Plugin plugin = pluginLoader.getPlugin(); pluginByIdMap.put(plugin.getPluginId(), plugin); // Register all of the detectors that this plugin contains boolean show = !pluginLoader.isCorePlugin(); for (Iterator<DetectorFactory> j = plugin.detectorFactoryIterator(); j.hasNext();) { DetectorFactory factory = j.next(); if (show) { jawsDebugMessage("Loading detector for " + factory.getFullName()); show = false; } registerDetector(factory); } I18N i18n = I18N.instance(); // Register the BugPatterns for (Iterator<BugPattern> j = plugin.bugPatternIterator(); j.hasNext();) { BugPattern bugPattern = j.next(); i18n.registerBugPattern(bugPattern); } // Register the BugCodes for (Iterator<BugCode> j = plugin.bugCodeIterator(); j.hasNext();) { BugCode bugCode = j.next(); i18n.registerBugCode(bugCode); } } } // vim:ts=4