@@ -17,11 +17,50 @@ | |||
<findbugs.version>1.3.9</findbugs.version> | |||
</properties> | |||
<dependencyManagement> | |||
<!-- Change versions for dependencies provided by sonar-plugin-api --> | |||
<dependencies> | |||
<dependency> | |||
<groupId>xerces</groupId> | |||
<artifactId>xercesImpl</artifactId> | |||
<version>2.6.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>xalan</groupId> | |||
<artifactId>xalan</artifactId> | |||
<version>2.6.0</version> | |||
</dependency> | |||
</dependencies> | |||
</dependencyManagement> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-plugin-api</artifactId> | |||
<version>${project.version}</version> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>xalan</groupId> | |||
<artifactId>xalan</artifactId> | |||
</exclusion> | |||
<exclusion> | |||
<groupId>jaxen</groupId> | |||
<artifactId>jaxen</artifactId> | |||
</exclusion> | |||
<exclusion> | |||
<groupId>dom4j</groupId> | |||
<artifactId>dom4j</artifactId> | |||
</exclusion> | |||
<exclusion> | |||
<groupId>xerces</groupId> | |||
<artifactId>xercesImpl</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.findbugs</groupId> | |||
<artifactId>findbugs</artifactId> | |||
<version>${findbugs.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.codehaus.sonar</groupId> | |||
@@ -63,12 +102,11 @@ | |||
<plugin> | |||
<groupId>org.codehaus.sonar</groupId> | |||
<artifactId>sonar-packaging-maven-plugin</artifactId> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<pluginKey>findbugs</pluginKey> | |||
<pluginName>Findbugs</pluginName> | |||
<pluginDescription><![CDATA[Analyze Java code with <a href="http://findbugs.sourceforge.net/">Findbugs</a> ${findbugs.version}.]]></pluginDescription> | |||
<pluginClass>org.sonar.plugins.findbugs.FindbugsPlugin</pluginClass> | |||
<useChildFirstClassLoader>true</useChildFirstClassLoader> | |||
</configuration> | |||
</plugin> | |||
<plugin> |
@@ -0,0 +1,67 @@ | |||
package org.sonar.plugins.findbugs; | |||
import org.sonar.api.BatchExtension; | |||
import org.sonar.api.profiles.RulesProfile; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.utils.SonarException; | |||
import org.sonar.plugins.findbugs.xml.ClassFilter; | |||
import org.sonar.plugins.findbugs.xml.FindBugsFilter; | |||
import org.sonar.plugins.findbugs.xml.Match; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.StringWriter; | |||
/** | |||
* @since 2.4 | |||
*/ | |||
public class FindbugsConfiguration implements BatchExtension { | |||
private Project project; | |||
private RulesProfile profile; | |||
private FindbugsProfileExporter exporter; | |||
public FindbugsConfiguration(Project project, RulesProfile profile, FindbugsProfileExporter exporter) { | |||
this.project = project; | |||
this.profile = profile; | |||
this.exporter = exporter; | |||
} | |||
public File getTargetXMLReport() { | |||
if (project.getConfiguration().getBoolean(FindbugsConstants.GENERATE_XML_KEY, FindbugsConstants.GENERATE_XML_DEFAULT_VALUE)) { | |||
return new File(project.getFileSystem().getSonarWorkingDirectory(), "findbugs-result.xml"); | |||
} | |||
return null; | |||
} | |||
public edu.umd.cs.findbugs.Project getFindbugsProject() { | |||
try { | |||
edu.umd.cs.findbugs.Project findbugsProject = new edu.umd.cs.findbugs.Project(); | |||
for (File dir : project.getFileSystem().getSourceDirs()) { | |||
findbugsProject.addSourceDir(dir.getAbsolutePath()); | |||
} | |||
findbugsProject.addFile(project.getFileSystem().getBuildOutputDir().getAbsolutePath()); | |||
findbugsProject.setCurrentWorkingDirectory(project.getFileSystem().getBuildDir()); | |||
return findbugsProject; | |||
} catch (Exception e) { | |||
throw new SonarException(e); | |||
} | |||
} | |||
public File saveIncludeConfigXml() throws IOException { | |||
StringWriter conf = new StringWriter(); | |||
exporter.exportProfile(profile, conf); | |||
return project.getFileSystem().writeToWorkingDirectory(conf.toString(), "findbugs-include.xml"); | |||
} | |||
public File saveExcludeConfigXml() throws IOException { | |||
FindBugsFilter findBugsFilter = new FindBugsFilter(); | |||
if (project.getExclusionPatterns() != null) { | |||
for (String exclusion : project.getExclusionPatterns()) { | |||
ClassFilter classFilter = new ClassFilter(FindbugsAntConverter.antToJavaRegexpConvertor(exclusion)); | |||
findBugsFilter.addMatch(new Match(classFilter)); | |||
} | |||
} | |||
return project.getFileSystem().writeToWorkingDirectory(findBugsFilter.toXml(), "findbugs-exclude.xml"); | |||
} | |||
} |
@@ -27,4 +27,10 @@ public final class FindbugsConstants { | |||
public static final String REPOSITORY_NAME = "Findbugs"; | |||
public static final String PLUGIN_NAME = "Findbugs"; | |||
public static final String PLUGIN_KEY = CoreProperties.FINDBUGS_PLUGIN; | |||
/** | |||
* @since 2.4 | |||
*/ | |||
public static final String GENERATE_XML_KEY = "sonar.findbugs.generateXml"; | |||
public static final boolean GENERATE_XML_DEFAULT_VALUE = true; // TODO should be false | |||
} |
@@ -0,0 +1,85 @@ | |||
package org.sonar.plugins.findbugs; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.io.output.NullOutputStream; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.BatchExtension; | |||
import org.sonar.api.utils.SonarException; | |||
import org.sonar.api.utils.TimeProfiler; | |||
import edu.umd.cs.findbugs.*; | |||
import edu.umd.cs.findbugs.annotations.Priority; | |||
import edu.umd.cs.findbugs.config.UserPreferences; | |||
import java.io.File; | |||
import java.io.OutputStream; | |||
import java.io.PrintStream; | |||
/** | |||
* @since 2.4 | |||
*/ | |||
public class FindbugsExecutor implements BatchExtension { | |||
private static Logger LOG = LoggerFactory.getLogger(FindbugsExecutor.class); | |||
private FindbugsConfiguration configuration; | |||
public FindbugsExecutor(FindbugsConfiguration configuration) { | |||
this.configuration = configuration; | |||
} | |||
public File execute() { | |||
TimeProfiler profiler = new TimeProfiler().start("Execute Findbugs"); | |||
ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader(); | |||
Thread.currentThread().setContextClassLoader(FindBugs2.class.getClassLoader()); | |||
OutputStream xmlOutput = null; | |||
try { | |||
final FindBugs2 engine = new FindBugs2(); | |||
Project project = configuration.getFindbugsProject(); | |||
engine.setProject(project); | |||
XMLBugReporter xmlBugReporter = new XMLBugReporter(project); | |||
xmlBugReporter.setPriorityThreshold(Priority.LOW.getPriorityValue()); | |||
// xmlBugReporter.setErrorVerbosity(BugReporter.SILENT); | |||
File xmlReport = configuration.getTargetXMLReport(); | |||
if (xmlReport != null) { | |||
LOG.info("Findbugs output report: " + xmlReport.getAbsolutePath()); | |||
xmlOutput = FileUtils.openOutputStream(xmlReport); | |||
} else { | |||
xmlOutput = new NullOutputStream(); | |||
} | |||
xmlBugReporter.setOutputStream(new PrintStream(xmlOutput)); | |||
engine.setBugReporter(xmlBugReporter); | |||
engine.setProject(project); | |||
engine.setDetectorFactoryCollection(DetectorFactoryCollection.instance()); | |||
UserPreferences userPreferences = UserPreferences.createDefaultUserPreferences(); | |||
userPreferences.setEffort(UserPreferences.EFFORT_DEFAULT); | |||
engine.addFilter(configuration.saveIncludeConfigXml().getAbsolutePath(), true); | |||
engine.addFilter(configuration.saveExcludeConfigXml().getAbsolutePath(), false); | |||
engine.setUserPreferences(userPreferences); | |||
engine.setAnalysisFeatureSettings(FindBugs.DEFAULT_EFFORT); | |||
engine.finishSettings(); | |||
engine.execute(); | |||
profiler.stop(); | |||
return xmlReport; | |||
} catch (Exception e) { | |||
throw new SonarException("Can not execute Findbugs", e); | |||
} finally { | |||
IOUtils.closeQuietly(xmlOutput); | |||
Thread.currentThread().setContextClassLoader(initialClassLoader); | |||
} | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
package org.sonar.plugins.findbugs; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.batch.Sensor; | |||
import org.sonar.api.batch.SensorContext; | |||
import org.sonar.api.profiles.RulesProfile; | |||
import org.sonar.api.resources.JavaFile; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.rules.Rule; | |||
import org.sonar.api.rules.RuleFinder; | |||
import org.sonar.api.rules.Violation; | |||
import java.util.List; | |||
/** | |||
* EXPERIMENTAL! | |||
* | |||
* @since 2.4 | |||
*/ | |||
public class FindbugsNativeSensor implements Sensor { | |||
private RulesProfile profile; | |||
private RuleFinder ruleFinder; | |||
private FindbugsExecutor executor; | |||
public FindbugsNativeSensor(RulesProfile profile, RuleFinder ruleFinder, FindbugsExecutor executor) { | |||
this.profile = profile; | |||
this.ruleFinder = ruleFinder; | |||
this.executor = executor; | |||
} | |||
public boolean shouldExecuteOnProject(Project project) { | |||
return project.getFileSystem().hasJavaSourceFiles() | |||
&& ( !profile.getActiveRulesByRepository(FindbugsConstants.REPOSITORY_KEY).isEmpty() || project.getReuseExistingRulesConfig()) | |||
&& project.getPom() != null && !StringUtils.equalsIgnoreCase(project.getPom().getPackaging(), "ear"); | |||
} | |||
public void analyse(Project project, SensorContext context) { | |||
FindbugsXmlReportParser reportParser = new FindbugsXmlReportParser(executor.execute()); | |||
List<FindbugsXmlReportParser.Violation> fbViolations = reportParser.getViolations(); | |||
for (FindbugsXmlReportParser.Violation fbViolation : fbViolations) { | |||
Rule rule = ruleFinder.findByKey(FindbugsConstants.REPOSITORY_KEY, fbViolation.getType()); | |||
JavaFile resource = new JavaFile(fbViolation.getSonarJavaFileKey()); | |||
if (context.getResource(resource) != null) { | |||
Violation violation = Violation.create(rule, resource).setLineId(fbViolation.getStart()).setMessage(fbViolation.getLongMessage()); | |||
context.saveViolation(violation); | |||
} | |||
} | |||
} | |||
@Override | |||
public String toString() { | |||
return getClass().getSimpleName(); | |||
} | |||
} |
@@ -69,8 +69,10 @@ public class FindbugsPlugin implements Plugin { | |||
public List<Class<? extends Extension>> getExtensions() { | |||
List<Class<? extends Extension>> list = new ArrayList<Class<? extends Extension>>(); | |||
list.add(FindbugsSensor.class); | |||
list.add(FindbugsMavenPluginHandler.class); | |||
list.add(FindbugsNativeSensor.class); | |||
list.add(FindbugsConfiguration.class); | |||
list.add(FindbugsExecutor.class); | |||
// list.add(FindbugsMavenPluginHandler.class); | |||
list.add(FindbugsRuleRepository.class); | |||
list.add(FindbugsProfileExporter.class); | |||
list.add(FindbugsProfileImporter.class); |
@@ -0,0 +1,42 @@ | |||
package org.sonar.plugins.findbugs; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.Test; | |||
import edu.umd.cs.findbugs.Project; | |||
import java.io.File; | |||
import static org.hamcrest.core.Is.is; | |||
import static org.junit.Assert.assertThat; | |||
import static org.junit.internal.matchers.StringContains.containsString; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class FindbugsExecutorTest { | |||
@Test | |||
public void canGenerateXMLReport() throws Exception { | |||
FindbugsConfiguration conf = mockConf(); | |||
File report = new File("target/test-tmp/findbugs-report.xml"); | |||
when(conf.getTargetXMLReport()).thenReturn(report); | |||
FindbugsExecutor executor = new FindbugsExecutor(conf); | |||
executor.execute(); | |||
assertThat(report.exists(), is(true)); | |||
assertThat(FileUtils.readFileToString(report), containsString("<BugInstance")); | |||
} | |||
private FindbugsConfiguration mockConf() throws Exception { | |||
FindbugsConfiguration conf = mock(FindbugsConfiguration.class); | |||
Project project = new Project(); | |||
project.addFile(new File("test-resources/classes").getCanonicalPath()); | |||
project.addSourceDir(new File("test-resources/src").getCanonicalPath()); | |||
project.setCurrentWorkingDirectory(new File("test-resources")); | |||
when(conf.getFindbugsProject()).thenReturn(project); | |||
when(conf.saveExcludeConfigXml()).thenReturn(new File("test-resources/findbugs-exclude.xml")); | |||
when(conf.saveIncludeConfigXml()).thenReturn(new File("test-resources/findbugs-include.xml")); | |||
return conf; | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
<FindBugsFilter/> |
@@ -0,0 +1,13 @@ | |||
class Hello { | |||
static String name; | |||
public void methodWithViolations(String n) { | |||
name = n; | |||
} | |||
@Override | |||
public boolean equals(Object obj) { | |||
return false; | |||
} | |||
} |