org.sonar.plugins.findbugs.FindbugsSensor 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 java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.batch.sensor.issue.NewIssue;
import org.sonar.api.batch.sensor.issue.NewIssueLocation;
import org.sonar.plugins.findbugs.language.Jsp;
import org.sonar.plugins.findbugs.language.scala.Scala;
import org.sonar.plugins.findbugs.resource.ByteCodeResourceLocator;
import org.sonar.plugins.findbugs.resource.ClassMetadataLoadingException;
import org.sonar.plugins.findbugs.resource.SmapParser;
import org.sonar.plugins.findbugs.rules.*;
import org.sonar.plugins.java.api.JavaResourceLocator;
public class FindbugsSensor implements Sensor {
private static final Logger LOG = LoggerFactory.getLogger(FindbugsSensor.class);
public static final String[] REPOS = {FindbugsRulesDefinition.REPOSITORY_KEY, FbContribRulesDefinition.REPOSITORY_KEY,
FindSecurityBugsRulesDefinition.REPOSITORY_KEY, FindSecurityBugsJspRulesDefinition.REPOSITORY_KEY,
FindSecurityBugsScalaRulesDefinition.REPOSITORY_KEY
};
private List repositories = new ArrayList();
private ActiveRules activeRules;
private FindbugsExecutor executor;
private final JavaResourceLocator javaResourceLocator;
private final ByteCodeResourceLocator byteCodeResourceLocator;
private final FileSystem fs;
private final SensorContext sensorContext;
protected final File classMappingFile;
protected PrintWriter classMappingWriter;
public FindbugsSensor(ActiveRules activeRules, SensorContext sensorContext,
FindbugsExecutor executor, JavaResourceLocator javaResourceLocator, FileSystem fs, ByteCodeResourceLocator byteCodeResourceLocator) {
this.activeRules = activeRules;
this.sensorContext = sensorContext;
this.executor = executor;
this.javaResourceLocator = javaResourceLocator;
this.byteCodeResourceLocator = byteCodeResourceLocator;
this.fs = fs;
registerRepositories(REPOS);
this.classMappingFile = new File(fs.workDir(), "class-mapping.csv");
try {
this.classMappingWriter = new PrintWriter(new FileOutputStream(classMappingFile));
} catch (FileNotFoundException e) {
}
}
public void registerRepositories(String... repos) {
Collections.addAll(repositories, repos);
}
private boolean hasActiveRules(String repository) {
return !activeRules.findByRepository(repository).isEmpty();
}
public List getRepositories() {
return repositories;
}
private boolean hasActiveFindbugsRules() {
return hasActiveRules(FindbugsRulesDefinition.REPOSITORY_KEY);
}
private boolean hasActiveFbContribRules() {
return hasActiveRules(FbContribRulesDefinition.REPOSITORY_KEY);
}
private boolean hasActiveFindSecBugsRules() {
boolean hasActiveFindSecBugsRules = hasActiveRules(FindSecurityBugsRulesDefinition.REPOSITORY_KEY);
boolean hasActiveFindSecBugsJspRules = hasActiveRules(FindSecurityBugsJspRulesDefinition.REPOSITORY_KEY);
boolean hasActiveFindSecBugsScalaRules = hasActiveRules(FindSecurityBugsScalaRulesDefinition.REPOSITORY_KEY);
SortedSet languages = fs.languages();
boolean hasActiveFindSecBugsJspRulesAndJspFiles = hasActiveFindSecBugsJspRules && languages.contains(Jsp.KEY);
boolean hasActiveFindSecBugsScalaRulesAndScalaFiles = hasActiveFindSecBugsScalaRules && languages.contains(Scala.KEY);
return hasActiveFindSecBugsRules || hasActiveFindSecBugsJspRulesAndJspFiles || hasActiveFindSecBugsScalaRulesAndScalaFiles;
}
@Override
public void execute(SensorContext context) {
if (!hasActiveFindbugsRules() && !hasActiveFbContribRules() && !hasActiveFindSecBugsRules()) {
return;
}
Collection collection = executor.execute(hasActiveFbContribRules(), hasActiveFindSecBugsRules());
try {
for (ReportedBug bugInstance : collection) {
try {
ActiveRule rule = null;
for (String repoKey : getRepositories()) {
rule = activeRules.findByInternalKey(repoKey, bugInstance.getType());
if (rule != null) {
break;
}
}
if (rule == null) {
// ignore violations from report, if rule not activated in Sonar
LOG.warn("Findbugs rule '{}' is not active in Sonar.", bugInstance.getType());
continue;
}
String className = bugInstance.getClassName();
String sourceFile = bugInstance.getSourceFile();
String longMessage = bugInstance.getMessage();
int line = bugInstance.getStartLine();
//Regular Java class mapped to their original .java
InputFile resource = byteCodeResourceLocator.findSourceFile(sourceFile, this.fs);
if (resource != null) {
insertIssue(rule, resource, line, longMessage, bugInstance);
continue;
}
//Locate the original class file
File classFile = findOriginalClassForBug(bugInstance.getClassFile());
if (classFile == null) {
LOG.warn("Unable to find the class " + bugInstance.getClassName());
continue;
}
// //If the class was an outer class, the source file will not be analog to the class name.
// //The original source file is available in the class file metadata.
// resource = byteCodeResourceLocator.findJavaOuterClassFile(className, classFile, this.fs);
// if (resource != null) {
// insertIssue(rule, resource, line, longMessage);
// continue;
// }
//More advanced mapping if the original source is not Java files
if (classFile != null) {
//Attempt to load SMAP debug metadata
try {
SmapParser.SmapLocation location = byteCodeResourceLocator.extractSmapLocation(className, line, classFile);
if (location != null) {
if (!location.isPrimaryFile) { //Avoid reporting issue in double when a source file was include inline
continue;
}
//SMAP was found
resource = byteCodeResourceLocator.findSourceFile(location.fileInfo.path, fs);
if (resource != null) {
insertIssue(rule, resource, location.line, longMessage, bugInstance);
continue;
}
} else {
//SMAP was not found or unparsable.. The orgininal source file will be guess based on the class name
resource = byteCodeResourceLocator.findTemplateFile(className, this.fs);
if (resource != null) {
insertIssue(rule, resource, line, longMessage, bugInstance);
continue;
}
}
} catch (ClassMetadataLoadingException e) {
LOG.warn("Failed to load the class file metadata", e);
}
}
LOG.warn("The class '" + className + "' could not be matched to its original source file. It might be a dynamically generated class.");
} catch (Exception e) {
String bugInstanceDebug = String.format("[BugInstance type=%s, class=%s, line=%s]", bugInstance.getType(), bugInstance.getClassName(), bugInstance.getStartLine());
LOG.warn("An error occurs while processing the bug instance " + bugInstanceDebug, e);
//Continue to the bug without aborting the report
}
}
}
finally {
if(classMappingWriter != null) {
classMappingWriter.flush();
classMappingWriter.close();
}
}
}
protected void insertIssue(ActiveRule rule, InputFile resource, int line, String message, ReportedBug bugInstance) {
NewIssue newIssue = sensorContext.newIssue().forRule(rule.ruleKey());
NewIssueLocation location = newIssue.newLocation()
.on(resource)
.at(resource.selectLine(line > 0 ? line : 1))
.message(message);
newIssue.at(location); //Primary location
newIssue.save();
writeDebugMappingToFile(bugInstance.getClassName(),bugInstance.getStartLine(), resource.relativePath(), line);
}
protected void writeDebugMappingToFile(String classFile, int classFileLine, String sourceFile, int sourceFileLine) {
if(classMappingWriter != null) {
classMappingWriter.println(classFile + ":" + classFileLine + "," + sourceFile + ":" + sourceFileLine);
}
}
/**
*
* @param className Class name
* @return File handle of the original class file analyzed
*/
private File findOriginalClassForBug(String className) {
String sourceFile = byteCodeResourceLocator.findSourceFileKeyByClassName(className,javaResourceLocator);
if (sourceFile == null) {
return null;
}
return new File(sourceFile);
}
@Override
public void describe(SensorDescriptor descriptor) {
descriptor.createIssuesForRuleRepositories(REPOS);
descriptor.onlyOnLanguages(FindbugsPlugin.SUPPORTED_JVM_LANGUAGES);
descriptor.name("FindBugs Sensor");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy