/* * Contributions to FindBugs * Copyright (C) 2010-2012, Andrey Loskutov * * 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 de.tobject.findbugs; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import java.util.SortedMap; import java.util.TreeMap; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IContributor; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import de.tobject.findbugs.io.IO; /** * Helper class to read contributions for the "detectorPlugins" extension point */ public class DetectorsExtensionHelper { private static final String DEFAULT_USE_PLUGIN_JAR = "."; private static final String EXTENSION_POINT_ID = FindbugsPlugin.PLUGIN_ID + ".findbugsPlugins"; private static final String LIBRARY_PATH = "libraryPath"; private static final String PLUGIN_ID = "fbPluginId"; /** key is the plugin id, value is the plugin library path */ private static SortedMap<String, String> contributedDetectors; /** key is the plugin id, value is the plugin library path */ public static synchronized SortedMap<String, String> getContributedDetectors() { if (contributedDetectors != null) { return new TreeMap<String, String>(contributedDetectors); } TreeMap<String, String> set = new TreeMap<String, String>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtensionPoint point = registry.getExtensionPoint(EXTENSION_POINT_ID); if (point == null) { return set; } IExtension[] extensions = point.getExtensions(); for (IExtension extension : extensions) { IConfigurationElement[] elements = extension.getConfigurationElements(); for (IConfigurationElement configElt : elements) { addContribution(set, configElt); } } contributedDetectors = set; return new TreeMap<String, String>(contributedDetectors); } private static void addContribution(TreeMap<String, String> set, IConfigurationElement configElt) { String libPathAsString; String pluginId; IContributor contributor = null; try { contributor = configElt.getContributor(); if (contributor == null) { throw new IllegalArgumentException("Null contributor"); } pluginId = configElt.getAttribute(PLUGIN_ID); if (pluginId == null) { throw new IllegalArgumentException("Missing '" + PLUGIN_ID + "'"); } libPathAsString = configElt.getAttribute(LIBRARY_PATH); if (libPathAsString == null) { throw new IllegalArgumentException("Missing '" + LIBRARY_PATH + "'"); } libPathAsString = resolveRelativePath(contributor, libPathAsString); if (libPathAsString == null) { throw new IllegalArgumentException("Failed to resolve library path for: " + pluginId); } if(set.containsKey(pluginId)) { throw new IllegalArgumentException("Duplicated '" + pluginId + "' contribution."); } set.put(pluginId, libPathAsString); } catch (Throwable e) { String cName = contributor != null ? contributor.getName() : "unknown contributor"; String message = "Failed to read contribution for '" + EXTENSION_POINT_ID + "' extension point from " + cName; FindbugsPlugin.getDefault().logException(e, message); } } /** * * @param contributor * non null * @param libPathAsString * non null * @return resolved absolute path for the detector package */ @CheckForNull private static String resolveRelativePath(IContributor contributor, String libPathAsString) { String bundleName = contributor.getName(); Bundle bundle = Platform.getBundle(bundleName); if (bundle == null) { return null; } File bundleFile; try { bundleFile = FileLocator.getBundleFile(bundle); } catch (IOException e) { FindbugsPlugin.getDefault().logException(e, "Failed to resolve detector library for " + bundle.getSymbolicName()); return null; } boolean runningInDebugger = Boolean.getBoolean("eclipse.pde.launch"); if (!DEFAULT_USE_PLUGIN_JAR.equals(libPathAsString)) { return new Path(bundleFile.getAbsolutePath()).append(libPathAsString).toOSString(); } if (!bundleFile.isDirectory()) { return bundleFile.getAbsolutePath(); } if (runningInDebugger) { // in case we are inside debugger & see bundle as directory return resolvePluginClassesDir(bundleName, bundleFile); } // it's a directory, and we are in the production environment. IllegalArgumentException e = new IllegalArgumentException("Failed to resolve detector library for " + bundle.getSymbolicName()); String message = "Failed to resolve detector library. '" + bundleFile + "' is a directory and can't be used as FindBugs detector package." + " Please specify '" + LIBRARY_PATH + "' argument as a relative path to the detectors jar file."; FindbugsPlugin.getDefault().logException(e, message); return null; } /** * Used for Eclipse instances running inside debugger. During development Eclipse plugins * are just directories. The code below tries to locate plugin's * "bin" directory. It doesn't work if the plugin build.properties are not * existing or contain invalid content */ @CheckForNull private static String resolvePluginClassesDir(String bundleName, File sourceDir) { if (sourceDir.listFiles() == null) { FindbugsPlugin.getDefault().logException(new IllegalStateException("No files in the bundle!"), "Failed to create temporary detector package for bundle " + sourceDir); return null; } String outputDir = getBuildDirectory(bundleName, sourceDir); if (outputDir.length() == 0) { FindbugsPlugin.getDefault().logException(new IllegalStateException("No output directory in build.properties"), "No output directory in build.properties " + sourceDir); return null; } File classDir = new File(sourceDir, outputDir); if (classDir.listFiles() == null) { FindbugsPlugin.getDefault().logException(new IllegalStateException("No files in the bundle output dir!"), "Failed to create temporary detector package for bundle " + sourceDir); return null; } File etcDir = new File(sourceDir, "etc"); if (etcDir.listFiles() == null) { FindbugsPlugin.getDefault().logException(new IllegalStateException("No files in the bundle etc dir!"), "Failed to create temporary detector package for bundle " + sourceDir); return null; } return classDir.getAbsolutePath(); } /** * @return possible deployment root directory of a plugin project */ @Nonnull private static String getBuildDirectory(String bundleName, File sourceDir) { Properties props = new Properties(); File buildProps = new File(sourceDir, "build.properties"); if (buildProps.isFile()) { FileInputStream inStream = null; try { inStream = new FileInputStream(buildProps); props.load(inStream); } catch (IOException e) { FindbugsPlugin.getDefault().logException(e, "Failed to read build.properties for bundle " + bundleName); } finally { IO.closeQuietly(inStream); } } // this works only for plugins which are self-contained and do not // include // any external libraries return props.getProperty("output..", ""); } }