org.sonar.plugins.findbugs.FindbugsExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sonar-findbugs-plugin Show documentation
Show all versions of sonar-findbugs-plugin Show documentation
SpotBugs is a program that uses static analysis to look for bugs in Java code. It can detect a variety of common coding mistakes, including thread synchronization problems, misuse of API methods.
/*
* SonarQube Findbugs Plugin
* Copyright (C) 2012 SonarSource
* [email protected]
*
* This program 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 3 of the License, or (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.plugins.findbugs;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import edu.umd.cs.findbugs.BugCollection;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.DetectorFactoryCollection;
import edu.umd.cs.findbugs.FindBugs;
import edu.umd.cs.findbugs.FindBugs2;
import edu.umd.cs.findbugs.Plugin;
import edu.umd.cs.findbugs.PluginException;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.XMLBugReporter;
import edu.umd.cs.findbugs.config.UserPreferences;
import edu.umd.cs.findbugs.plugins.DuplicatePluginIdException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.config.Configuration;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ScannerSide
public class FindbugsExecutor {
private static final String FINDBUGS_CORE_PLUGIN_ID = "edu.umd.cs.findbugs.plugins.core";
private static final Logger LOG = LoggerFactory.getLogger(FindbugsExecutor.class);
public static final List EXISTING_FINDBUGS_REPORT_PATHS = Arrays.asList("/target/findbugsXml.xml","/target/spotbugsXml.xml");
private FileSystem fs;
private Configuration config;
/**
* Map of priority level names to their numeric values.
*/
private static Map priorityNameToValueMap = new HashMap();
static {
priorityNameToValueMap.put("high", Priorities.HIGH_PRIORITY);
priorityNameToValueMap.put("medium", Priorities.NORMAL_PRIORITY);
priorityNameToValueMap.put("low", Priorities.LOW_PRIORITY);
priorityNameToValueMap.put("experimental", Priorities.EXP_PRIORITY);
}
private static final Integer DEFAULT_PRIORITY = Priorities.NORMAL_PRIORITY;
private final FindbugsConfiguration configuration;
public FindbugsExecutor(FindbugsConfiguration configuration, FileSystem fs, Configuration config) {
this.configuration = configuration;
this.fs = fs;
this.config = config;
}
@VisibleForTesting
Collection execute() {
return execute(true);
}
public Collection execute(boolean useAllPlugin) {
return execute(useAllPlugin,useAllPlugin);
}
public Collection execute(boolean useFbContrib, boolean useFindSecBugs) {
// We keep a handle on the current security manager because FB plays with it and we need to restore it before shutting down the executor
// service
SecurityManager currentSecurityManager = System.getSecurityManager();
ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(FindBugs2.class.getClassLoader());
// This is a dirty workaround, but unfortunately there is no other way to make Findbugs generate english messages only - see SONARJAVA-380
Locale initialLocale = Locale.getDefault();
Locale.setDefault(Locale.ENGLISH);
OutputStream xmlOutput = null;
Collection customPlugins = null;
ExecutorService executorService = Executors.newSingleThreadExecutor();
try (FindBugs2 engine = new FindBugs2(); Project project = new Project()) {
configuration.initializeFindbugsProject(project);
if(project.getFileCount() == 0) {
LOG.info("Findbugs analysis skipped for this project.");
return new ArrayList<>();
}
customPlugins = loadFindbugsPlugins(useFbContrib,useFindSecBugs);
disableUpdateChecksOnEveryPlugin();
engine.setProject(project);
XMLBugReporter xmlBugReporter = new XMLBugReporter(project);
xmlBugReporter.setPriorityThreshold(determinePriorityThreshold());
xmlBugReporter.setAddMessages(true);
File xmlReport = configuration.getTargetXMLReport();
LOG.info("Findbugs output report: " + xmlReport.getAbsolutePath());
xmlOutput = FileUtils.openOutputStream(xmlReport);
xmlBugReporter.setOutputStream(new PrintStream(xmlOutput));
engine.setBugReporter(xmlBugReporter);
UserPreferences userPreferences = UserPreferences.createDefaultUserPreferences();
userPreferences.setEffort(configuration.getEffort());
engine.setUserPreferences(userPreferences);
engine.addFilter(configuration.saveIncludeConfigXml().getAbsolutePath(), true);
for (File filterFile : configuration.getExcludesFilters()) {
if (filterFile.isFile()) {
LOG.info("Use filter-file: {}", filterFile);
engine.addFilter(filterFile.getAbsolutePath(), false);
} else {
LOG.warn("FindBugs filter-file not found: {}", filterFile);
}
}
engine.setDetectorFactoryCollection(DetectorFactoryCollection.instance());
engine.setAnalysisFeatureSettings(FindBugs.DEFAULT_EFFORT);
engine.finishSettings();
//Load findbugs report location
List potentialReportPaths = new ArrayList<>();
potentialReportPaths.addAll(EXISTING_FINDBUGS_REPORT_PATHS);
String[] paths = config.getStringArray(FindbugsConstants.REPORT_PATHS);
if(paths != null) potentialReportPaths.addAll(Arrays.asList(paths));
boolean foundExistingReport = false;
//Look for existing reports relative to subproject directory
reportPaths : for(String potentialPath : potentialReportPaths) {
File findbugsReport = new File(fs.baseDir(), potentialPath);
// File.length() is unspecified for directories
if(findbugsReport.exists() && !findbugsReport.isDirectory() && findbugsReport.length() > 0) {
LOG.info("FindBugs report is already generated {}. Reusing the report.",findbugsReport.getAbsolutePath());
xmlBugReporter.getBugCollection().readXML(new FileReader(findbugsReport));
foundExistingReport = true;
break reportPaths;
}
}
if(!foundExistingReport) { //Avoid rescanning the project if FindBugs was run already
executorService.submit(new FindbugsTask(engine)).get(configuration.getTimeout(), TimeUnit.MILLISECONDS);
}
return toReportedBugs(xmlBugReporter.getBugCollection());
} catch (TimeoutException e) {
throw new IllegalStateException("Can not execute Findbugs with a timeout threshold value of " + configuration.getTimeout() + " milliseconds", e);
} catch (Exception e) {
throw new IllegalStateException("Can not execute Findbugs", e);
} finally {
// we set back the original security manager BEFORE shutting down the executor service, otherwise there's a problem with Java 5
System.setSecurityManager(currentSecurityManager);
resetCustomPluginList(customPlugins);
executorService.shutdown();
IOUtils.closeQuietly(xmlOutput);
Thread.currentThread().setContextClassLoader(initialClassLoader);
Locale.setDefault(initialLocale);
}
}
private static Collection toReportedBugs(BugCollection bugCollection) {
// We need to retrieve information such as the message before we shut everything down as we will lose any custom
// bug messages
final Collection bugs = new ArrayList();
for (final BugInstance bugInstance : bugCollection) {
if (bugInstance.getPrimarySourceLineAnnotation() == null) {
LOG.warn("No source line for " + bugInstance.getType());
continue;
}
bugs.add(new ReportedBug(bugInstance));
}
return bugs;
}
private Integer determinePriorityThreshold() {
Integer integer = priorityNameToValueMap.get(configuration.getConfidenceLevel());
if (integer == null) {
integer = DEFAULT_PRIORITY;
}
return integer;
}
private static class FindbugsTask implements Callable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy