sonar findbugs plugin源码研究
首先貼一下findbugs plugin 代碼目錄:
代碼:
代碼里面有很幾個關(guān)于plugin定義的關(guān)鍵類:
language包下面定義了掃描jsp 的思路:
public class Jspextends AbstractLanguage {public static final String KEY = "jsp";public static final String NAME = "JSP";public static final String FILE_SUFFIXES_KEY = "sonar.jsp.file.suffixes";public static final String DEFAULT_FILE_SUFFIXES = ".jsp";private final Settings settings;public Jsp(Settings settings){super("jsp", "JSP");this.settings = settings;}public String[] getFileSuffixes(){String[] suffixes = filterEmptyStrings(this.settings.getStringArray("sonar.jsp.file.suffixes"));if (suffixes.length == 0) {suffixes = StringUtils.split(".jsp", ",");}return suffixes;}private static String[] filterEmptyStrings(String[] stringArray){List<String> nonEmptyStrings = Lists.newArrayList();for (String string : stringArray) {if (StringUtils.isNotBlank(string.trim())) {nonEmptyStrings.add(string.trim());}}return (String[])nonEmptyStrings.toArray(new String[nonEmptyStrings.size()]);} }JspCodeColorizerFormat 類定義了如何識別標簽掃描jsp
public class JspCodeColorizerFormatextends CodeColorizerFormat {private final List<Tokenizer> tokenizers = new ArrayList();public JspCodeColorizerFormat(){super("jsp");String tagAfter = "</span>";this.tokenizers.add(new RegexpTokenizer("<span class=\"k\">", tagAfter, "</?[:\\w]+>?"));this.tokenizers.add(new RegexpTokenizer("<span class=\"k\">", tagAfter, ">"));this.tokenizers.add(new RegexpTokenizer("<span class=\"j\">", tagAfter, "<!DOCTYPE.*>"));this.tokenizers.add(new MultilinesDocTokenizer("<!--", "-->", "<span class=\"j\">", tagAfter));this.tokenizers.add(new MultilinesDocTokenizer("<%--", "--%>", "<span class=\"j\">", tagAfter));this.tokenizers.add(new MultilinesDocTokenizer("<%@", "%>", "<span class=\"a\">", tagAfter));this.tokenizers.add(new MultilinesDocTokenizer("<%", "%>", "<span class=\"a\">", tagAfter));this.tokenizers.add(new StringTokenizer("<span class=\"s\">", tagAfter));}public List<Tokenizer> getTokenizers(){return this.tokenizers;} }profiles 包中定義了規(guī)則相關(guān):
FindBugs + FB-Contrib 規(guī)則,針對語言java
定義FindBugs 規(guī)則,針對語言java
public class FindbugsProfileextends ProfileDefinition {private static final String FINDBUGS_PROFILE_NAME = "FindBugs";private final FindbugsProfileImporter importer;public FindbugsProfile(FindbugsProfileImporter importer){this.importer = importer;}public RulesProfile createProfile(ValidationMessages messages){Reader findbugsProfile = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/plugins/findbugs/profile-findbugs-only.xml"));RulesProfile profile = this.importer.importProfile(findbugsProfile, messages);profile.setLanguage("java");profile.setName("FindBugs");return profile;} }定義了規(guī)則FindBugs Security Audit,針對語言java
public class FindbugsSecurityAuditProfileextends ProfileDefinition {private static final String FINDBUGS_SECURITY_AUDIT_PROFILE_NAME = "FindBugs Security Audit";private final FindbugsProfileImporter importer;public FindbugsSecurityAuditProfile(FindbugsProfileImporter importer){this.importer = importer;}public RulesProfile createProfile(ValidationMessages messages){Reader findbugsProfile = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/plugins/findbugs/profile-findbugs-security-audit.xml"));RulesProfile profile = this.importer.importProfile(findbugsProfile, messages);profile.setLanguage("java");profile.setName("FindBugs Security Audit");return profile;} }定義檢查規(guī)則FindBugs Security JSP 針對語言:jsp
public class FindbugsSecurityJspProfileextends ProfileDefinition {private static final String FINDBUGS_SECURITY_JSP_PROFILE_NAME = "FindBugs Security JSP";private final FindbugsProfileImporter importer;public FindbugsSecurityJspProfile(FindbugsProfileImporter importer){this.importer = importer;}public RulesProfile createProfile(ValidationMessages messages){Reader findbugsProfile = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/plugins/findbugs/profile-findbugs-security-jsp.xml"));RulesProfile profile = this.importer.importProfile(findbugsProfile, messages);profile.setLanguage("jsp");profile.setName("FindBugs Security JSP");return profile;}定義規(guī)則:FindBugs Security Minimal 針對語言java
public class FindbugsSecurityMinimalProfileextends ProfileDefinition {private static final String FINDBUGS_SECURITY_AUDIT_PROFILE_NAME = "FindBugs Security Minimal";private final FindbugsProfileImporter importer;public FindbugsSecurityMinimalProfile(FindbugsProfileImporter importer){this.importer = importer;}public RulesProfile createProfile(ValidationMessages messages){Reader findbugsProfile = new InputStreamReader(getClass().getResourceAsStream("/org/sonar/plugins/findbugs/profile-findbugs-security-minimal.xml"));RulesProfile profile = this.importer.importProfile(findbugsProfile, messages);profile.setLanguage("java");profile.setName("FindBugs Security Minimal");return profile;} }resource包下面定義的類,是加載掃描jsp 相關(guān)java類
public class ByteCodeResourceLocatorimplements BatchExtension {private static final Logger LOG = LoggerFactory.getLogger(ByteCodeResourceLocator.class);private static final String[] SOURCE_DIRECTORIES = { "src/main/java", "src/main/webapp", "src/main/resources", "src", "/src/java" };public InputFile findJavaClassFile(String className, FileSystem fs){int indexDollarSign = className.indexOf("$");if (indexDollarSign != -1) {className = className.substring(0, indexDollarSign);}return buildInputFile(className.replaceAll("\\.", "/") + ".java", fs);}public InputFile findJavaOuterClassFile(String className, File classFile, FileSystem fs){try{InputStream in = new FileInputStream(classFile);Throwable localThrowable4 = null;try{DebugExtensionExtractor debug = new DebugExtensionExtractor();String source = debug.getDebugSourceFromClass(in);if (source == null) {return null;}String newClassName = FilenameUtils.getBaseName(source);String packagePrefix = className.lastIndexOf(".") != -1 ? FilenameUtils.getBaseName(className) + "." : "";String fullClassName = packagePrefix + newClassName;return findJavaClassFile(fullClassName, fs);}catch (Throwable localThrowable2){localThrowable4 = localThrowable2;throw localThrowable2;}finally{if (in != null) {if (localThrowable4 != null) {try{in.close();}catch (Throwable localThrowable3){localThrowable4.addSuppressed(localThrowable3);}} else {in.close();}}}return null;}catch (IOException e){LOG.warn("An error occurs while opening classfile : " + classFile.getPath());}}public InputFile findTemplateFile(String className, FileSystem fs){List<String> potentialJspFilenames = new ArrayList();if (className.startsWith("jsp_servlet")){String jspFile = className.substring(11).replaceFirst("\\.__([^\\.]+)$", "/$1\\.jsp").replace("._", "/");potentialJspFilenames.add(jspFile);}String jspFileFromClass;if (className.endsWith("_jsp")){jspFileFromClass = JasperUtils.decodeJspClassName(className);potentialJspFilenames.add(jspFileFromClass);for (String packageName : Arrays.asList(new String[] { "jsp/", "org/apache/jsp/" })) {if (jspFileFromClass.startsWith(packageName)) {potentialJspFilenames.add(jspFileFromClass.substring(packageName.length()));}}}for (String jspFilename : potentialJspFilenames){InputFile file = buildInputFile(jspFilename, fs);if (file != null) {return file;}}return null;}public InputFile buildInputFile(String fileName, FileSystem fs){for (String sourceDir : SOURCE_DIRECTORIES){Iterable<InputFile> files = fs.inputFiles(fs.predicates().hasRelativePath(sourceDir + "/" + fileName));Iterator localIterator = files.iterator();if (localIterator.hasNext()){InputFile f = (InputFile)localIterator.next();return f;}}return null;}@Nullablepublic SmapParser.SmapLocation extractSmapLocation(String className, int originalLine, File classFile){String smap;try{InputStream in = new FileInputStream(classFile);Throwable localThrowable7 = null;try{DebugExtensionExtractor debug = new DebugExtensionExtractor();smap = debug.getDebugExtFromClass(in);if (smap != null) {return getJspLineNumberFromSmap(smap, Integer.valueOf(originalLine));}}catch (Throwable localThrowable2){localThrowable7 = localThrowable2;throw localThrowable2;}finally{if (in != null) {if (localThrowable7 != null) {try{in.close();}catch (Throwable localThrowable3){localThrowable7.addSuppressed(localThrowable3);}} else {in.close();}}}}catch (IOException e){LOG.warn("An error occurs while opening classfile : " + classFile.getPath());}LOG.debug("No smap file found for the class: " + className);File smapFile = new File(classFile.getPath() + ".smap");if (smapFile.exists()) {try{Object smapInputStream = new FileInputStream(smapFile);localThrowable2 = null;try{return getJspLineNumberFromSmap(IOUtils.toString((InputStream)smapInputStream), Integer.valueOf(originalLine));}catch (Throwable localThrowable5){localThrowable2 = localThrowable5;throw localThrowable5;}finally{if (smapInputStream != null) {if (localThrowable2 != null) {try{((InputStream)smapInputStream).close();}catch (Throwable localThrowable6){localThrowable2.addSuppressed(localThrowable6);}} else {((InputStream)smapInputStream).close();}}}LOG.debug("No smap mapping found.");}catch (IOException e){LOG.debug("Unable to open smap file : " + smapFile.getAbsolutePath());}}return null;}private SmapParser.SmapLocation getJspLineNumberFromSmap(String smap, Integer originalLine)throws IOException{SmapParser parser = new SmapParser(smap);return parser.getSmapLocation(originalLine);} } public class SmapParser {private String javaFilename;private final Map<Integer, FileInfo> fileinfo = new HashMap();private final Map<Integer, int[]> java2jsp = new HashMap();private static final Pattern LINE_INFO_PATTERN = Pattern.compile("([0-9]+)(?:#([0-9]+))?(?:,([0-9]+))?:([0-9]+)(?:,([0-9]+))?");private static String getLine(BufferedReader reader)throws IOException{String s = reader.readLine();if (s == null) {throw new IOException("EOF parsing SMAP");}return s;}public SmapParser(String smap)throws IOException{BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(smap.getBytes())));String header = getLine(reader);this.javaFilename = getLine(reader);String jsp = getLine(reader);String stratum = getLine(reader);String f = getLine(reader);if ((!header.equals("SMAP")) || (!stratum.startsWith("*S ")) || (!f.equals("*F"))) {throw new IllegalArgumentException("Unexpected SMAP file format");}String line;while (((line = getLine(reader)) != null) && (!line.equals("*L"))){String path = null;if (line.startsWith("+ ")){path = getLine(reader);line = line.substring(2);}int pos = line.indexOf(" ");int fileNum = Integer.parseInt(line.substring(0, pos));String name = line.substring(pos + 1);this.fileinfo.put(Integer.valueOf(fileNum), new FileInfo(name, path == null ? name : path));}int lastLFI = 0;while (((line = getLine(reader)) != null) && (!line.equals("*E"))) {if (!line.startsWith("*")){Matcher m = LINE_INFO_PATTERN.matcher(line);if (!m.matches()) {throw new IllegalArgumentException(line);}int inputStartLine = Integer.parseInt(m.group(1));int lineFileID = m.group(2) == null ? lastLFI : Integer.parseInt(m.group(2));int repeatCount = m.group(3) == null ? 1 : Integer.parseInt(m.group(3));int outputStartLine = Integer.parseInt(m.group(4));int outputLineIncrement = m.group(5) == null ? 1 : Integer.parseInt(m.group(5));for (int i = 0; i < repeatCount; i++){int[] inputMapping = { lineFileID, inputStartLine + i };int baseOL = outputStartLine + i * outputLineIncrement;for (int ol = baseOL; ol < baseOL + outputLineIncrement; ol++) {if (!this.java2jsp.containsKey(Integer.valueOf(ol))) {this.java2jsp.put(Integer.valueOf(ol), inputMapping);}}}lastLFI = lineFileID;}}}public String getJavaFilename(){return this.javaFilename;}public String getScriptFilename(int fileIndex){FileInfo f = (FileInfo)this.fileinfo.get(Integer.valueOf(fileIndex));return f.name;}public int[] getScriptLineNumber(Integer lineNo){return (int[])this.java2jsp.get(lineNo);}public SmapLocation getSmapLocation(Integer lineNo){int[] origSource = (int[])this.java2jsp.get(lineNo);FileInfo info = (FileInfo)this.fileinfo.get(Integer.valueOf(origSource[0]));return new SmapLocation(info, origSource[1], origSource[0] == 0);}public static class FileInfo{public final String name;public final String path;public FileInfo(String name, String path){this.name = name;this.path = path;}}public static class SmapLocation{public final SmapParser.FileInfo fileInfo;public final int line;public final boolean isPrimaryFile;public SmapLocation(SmapParser.FileInfo fileInfo, int line, boolean isPrimaryFile){this.fileInfo = fileInfo;this.line = line;this.isPrimaryFile = isPrimaryFile;}} } public static String decodeJspClassName(String className){className = className.replaceAll("\\.", "/");for (char ch = '\000'; ch < ''; ch = (char)(ch + '\001')) {if (((isPrintableChar(ch)) && (!Character.isJavaIdentifierPart(ch))) || (ch == '_')) {className = className.replace(mangleChar(ch), "" + ch);}}return className.replaceAll("_jsp", ".jsp");}public static final String mangleChar(char ch){char[] result = new char[5];result[0] = '_';result[1] = Character.forDigit(ch >> '\f' & 0xF, 16);result[2] = Character.forDigit(ch >> '\b' & 0xF, 16);result[3] = Character.forDigit(ch >> '\004' & 0xF, 16);result[4] = Character.forDigit(ch & 0xF, 16);return new String(result);}public static boolean isPrintableChar(char c){Character.UnicodeBlock block = Character.UnicodeBlock.of(c);return (!Character.isISOControl(c)) && (c != 65535) && (block != null) && (block != Character.UnicodeBlock.SPECIALS);} }xml 包中定義一些規(guī)則相關(guān)的bean
FindbugsPlugin 定義類:
加載各種定義的 profile類,然后就是FindbugsProfileExporter和FindbugsProfileImporter,規(guī)則文件的到處生產(chǎn)和規(guī)則文件的導入類:
FindbugsProfileExporter類:
FindbugsProfileImporter規(guī)則導入類:
public class FindbugsProfileImporterextends ProfileImporter {private final RuleFinder ruleFinder;private static final Logger LOG = LoggerFactory.getLogger(FindbugsProfileImporter.class);public FindbugsProfileImporter(RuleFinder ruleFinder){super("findbugs", "FindBugs");setSupportedLanguages(new String[] { "java", "jsp" });this.ruleFinder = ruleFinder;}public RulesProfile importProfile(Reader findbugsConf, ValidationMessages messages){RulesProfile profile = RulesProfile.create();try{XStream xStream = FindBugsFilter.createXStream();FindBugsFilter filter = (FindBugsFilter)xStream.fromXML(findbugsConf);activateRulesByCategory(profile, filter, messages);activateRulesByCode(profile, filter, messages);activateRulesByPattern(profile, filter, messages);return profile;}catch (Exception e){String errorMessage = "The Findbugs configuration file is not valid";messages.addErrorText(errorMessage + " : " + e.getMessage());LOG.error(errorMessage, e);}return profile;}private void activateRulesByPattern(RulesProfile profile, FindBugsFilter filter, ValidationMessages messages){for (Map.Entry<String, String> patternLevel : filter.getPatternLevels(new FindbugsLevelUtils()).entrySet()){Rule rule = this.ruleFinder.findByKey("findbugs", (String)patternLevel.getKey());if (rule == null){rule = this.ruleFinder.findByKey("fb-contrib", (String)patternLevel.getKey());if (rule == null){rule = this.ruleFinder.findByKey("findsecbugs", (String)patternLevel.getKey());if (rule == null) {rule = this.ruleFinder.findByKey("findsecbugs-jsp", (String)patternLevel.getKey());}}}if (rule != null) {profile.activateRule(rule, getPriorityFromSeverity((String)patternLevel.getValue()));} else {messages.addWarningText("Unable to activate unknown rule : '" + (String)patternLevel.getKey() + "'");}}}private void activateRulesByCode(RulesProfile profile, FindBugsFilter filter, ValidationMessages messages){for (Map.Entry<String, String> codeLevel : filter.getCodeLevels(new FindbugsLevelUtils()).entrySet()){boolean someRulesHaveBeenActivated = false;for (Rule rule : rules()) {if ((rule.getKey().equals(codeLevel.getKey())) || (StringUtils.startsWith(rule.getKey(), (String)codeLevel.getKey() + "_"))){someRulesHaveBeenActivated = true;profile.activateRule(rule, getPriorityFromSeverity((String)codeLevel.getValue()));}}if (!someRulesHaveBeenActivated) {messages.addWarningText("Unable to find any rules associated to code : '" + (String)codeLevel.getKey() + "'");}}}private void activateRulesByCategory(RulesProfile profile, FindBugsFilter filter, ValidationMessages messages){for (Map.Entry<String, String> categoryLevel : filter.getCategoryLevels(new FindbugsLevelUtils()).entrySet()){boolean someRulesHaveBeenActivated = false;String sonarCateg = FindbugsCategory.findbugsToSonar((String)categoryLevel.getKey());for (Rule rule : rules()) {if ((sonarCateg != null) && (rule.getName().startsWith(sonarCateg))){someRulesHaveBeenActivated = true;profile.activateRule(rule, getPriorityFromSeverity((String)categoryLevel.getValue()));}}if (!someRulesHaveBeenActivated) {messages.addWarningText("Unable to find any rules associated to category : '" + (String)categoryLevel.getKey() + "'");}}}private static RulePriority getPriorityFromSeverity(String severity){if ("INFO".equals(severity)) {return RulePriority.INFO;}if ("MAJOR".equals(severity)) {return RulePriority.MAJOR;}if ("BLOCKER".equals(severity)) {return RulePriority.BLOCKER;}return null;}private Iterable<Rule> rules(){return Iterables.concat(this.ruleFinder.findAll(RuleQuery.create().withRepositoryKey("findbugs")), this.ruleFinder.findAll(RuleQuery.create().withRepositoryKey("fb-contrib")), this.ruleFinder.findAll(RuleQuery.create().withRepositoryKey("findsecbugs")), this.ruleFinder.findAll(RuleQuery.create().withRepositoryKey("findsecbugs-jsp")));} }然后加載findbugsSensor類:findbugsSensor定義了,各個profile的引入和掃描jsp文件以及對應的java 的class文件。
package org.sonar.plugins.findbugs;import java.io.File; import java.util.Collection; import java.util.List; 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.api.profiles.RulesProfile; import org.sonar.plugins.findbugs.resource.ByteCodeResourceLocator; import org.sonar.plugins.findbugs.resource.ClassMetadataLoadingException; import org.sonar.plugins.findbugs.resource.SmapParser.FileInfo; import org.sonar.plugins.findbugs.resource.SmapParser.SmapLocation; import org.sonar.plugins.java.api.JavaResourceLocator;public class FindbugsSensorimplements Sensor {private static final Logger LOG = LoggerFactory.getLogger(FindbugsSensor.class);private RulesProfile profile;private ActiveRules ruleFinder;private FindbugsExecutor executor;private final JavaResourceLocator javaResourceLocator;private final ByteCodeResourceLocator byteCodeResourceLocator;private final FileSystem fs;private final SensorContext sensorContext;public FindbugsSensor(RulesProfile profile, ActiveRules ruleFinder, SensorContext sensorContext, FindbugsExecutor executor, JavaResourceLocator javaResourceLocator, FileSystem fs, ByteCodeResourceLocator byteCodeResourceLocator){this.profile = profile;this.ruleFinder = ruleFinder;this.sensorContext = sensorContext;this.executor = executor;this.javaResourceLocator = javaResourceLocator;this.byteCodeResourceLocator = byteCodeResourceLocator;this.fs = fs;}private boolean hasActiveFindbugsRules(){return !this.profile.getActiveRulesByRepository("findbugs").isEmpty();}private boolean hasActiveFbContribRules(){return !this.profile.getActiveRulesByRepository("fb-contrib").isEmpty();}private boolean hasActiveFindSecBugsRules(){return !this.profile.getActiveRulesByRepository("findsecbugs").isEmpty();}private boolean hasActiveFindSecBugsJspRules(){return !this.profile.getActiveRulesByRepository("findsecbugs-jsp").isEmpty();}public void execute(SensorContext context){if ((!hasActiveFindbugsRules()) && (!hasActiveFbContribRules()) && (!hasActiveFindSecBugsRules()) && (!hasActiveFindSecBugsJspRules())) {return;}Collection<ReportedBug> collection = this.executor.execute(hasActiveFbContribRules(), (hasActiveFindSecBugsRules()) || (hasActiveFindSecBugsJspRules()));for (ReportedBug bugInstance : collection) {try{String[] repos = { "findbugs", "fb-contrib", "findsecbugs", "findsecbugs-jsp" };ActiveRule rule = null;for (String repoKey : repos){rule = this.ruleFinder.findByInternalKey(repoKey, bugInstance.getType());if (rule != null) {break;}}if (rule == null){LOG.warn("Findbugs rule '{}' is not active in Sonar.", bugInstance.getType());}else{String className = bugInstance.getClassName();String longMessage = bugInstance.getMessage();int line = bugInstance.getStartLine();InputFile resource = null;resource = this.byteCodeResourceLocator.findJavaClassFile(className, this.fs);if (resource != null){insertIssue(rule, resource, line, longMessage);}else{File classFile = findOriginalClassForBug(bugInstance.getClassName());resource = this.byteCodeResourceLocator.findJavaOuterClassFile(className, classFile, this.fs);if (resource != null) {insertIssue(rule, resource, line, longMessage);} else if (classFile != null) {try{SmapParser.SmapLocation location = this.byteCodeResourceLocator.extractSmapLocation(className, line, classFile);if (location != null){if (!location.isPrimaryFile) {continue;}resource = this.byteCodeResourceLocator.buildInputFile(location.fileInfo.path, this.fs);if (resource != null) {insertIssue(rule, resource, location.line, longMessage);}}else{resource = this.byteCodeResourceLocator.findTemplateFile(className, this.fs);if (resource != null){insertIssue(rule, resource, line, longMessage);continue;}}}catch (ClassMetadataLoadingException e){LOG.warn("Failed to load the class file metadata", e);}} else {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]", new Object[] { bugInstance.getType(), bugInstance.getClassName(), Integer.valueOf(bugInstance.getStartLine()) });LOG.warn("An error occurs while processing the bug instance " + bugInstanceDebug, e);}}}protected void insertIssue(ActiveRule rule, InputFile resource, int line, String message){NewIssue newIssue = this.sensorContext.newIssue().forRule(rule.ruleKey());NewIssueLocation location = newIssue.newLocation().on(resource).at(resource.selectLine(line > 0 ? line : 1)).message(message);newIssue.at(location);newIssue.save();}private File findOriginalClassForBug(String className){String classFile = className.replaceAll("\\.", "/").concat(".class");for (File classPath : this.javaResourceLocator.classpath()) {if (classPath.isDirectory()){File testClassFile = new File(classPath, classFile);if (testClassFile.exists()) {return testClassFile;}}}return null;}public void describe(SensorDescriptor descriptor){descriptor.onlyOnLanguages(new String[] { "java", "jsp" });descriptor.name("FindBugs Sensor");} }FindbugsConfiguration類,這個類中定義了findbugs plugin相關(guān)工作空間,排除java文件,掃描生成文件等等。
public class FindbugsConfiguration {private static final Logger LOG = LoggerFactory.getLogger(FindbugsExecutor.class);private final FileSystem fileSystem;private final Settings settings;private final RulesProfile profile;private final FindbugsProfileExporter exporter;private final JavaResourceLocator javaResourceLocator;private File jsr305Lib;private File annotationsLib;private File fbContrib;private File findSecBugs;public FindbugsConfiguration(FileSystem fileSystem, Settings settings, RulesProfile profile, FindbugsProfileExporter exporter, JavaResourceLocator javaResourceLocator){this.fileSystem = fileSystem;this.settings = settings;this.profile = profile;this.exporter = exporter;this.javaResourceLocator = javaResourceLocator;}public File getTargetXMLReport(){return new File(this.fileSystem.workDir(), "findbugs-result.xml");}public Project getFindbugsProject()throws IOException{Project findbugsProject = new Project();for (Iterator localIterator1 = getSourceFiles().iterator(); localIterator1.hasNext();){file = (File)localIterator1.next();if (FilenameUtils.getExtension(file.getName()).equals("java")) {findbugsProject.addFile(file.getCanonicalPath());}}File file;Object classFilesToAnalyze = new ArrayList(this.javaResourceLocator.classFilesToAnalyze());for (File file : this.javaResourceLocator.classpath()){if (file.isDirectory()) {((List)classFilesToAnalyze).addAll(scanForAdditionalClasses(file));}findbugsProject.addAuxClasspathEntry(file.getCanonicalPath());}boolean hasJspFiles = this.fileSystem.hasFiles(this.fileSystem.predicates().hasLanguage("jsp"));boolean hasPrecompiledJsp = false;for (File classToAnalyze : (List)classFilesToAnalyze){String absolutePath = classToAnalyze.getCanonicalPath();if ((hasJspFiles) && (!hasPrecompiledJsp) && ((absolutePath.endsWith("_jsp.class")) || (absolutePath.contains("/jsp_servlet/")))) {hasPrecompiledJsp = true;}findbugsProject.addFile(absolutePath);}if (((List)classFilesToAnalyze).isEmpty()){LOG.warn("Findbugs needs sources to be compiled. Please build project before executing sonar or check the location of compiled classes to make it possible for Findbugs to analyse your project.");if (hasSourceFiles()) {throw new IllegalStateException("This project contains Java source files that are not compiled.");}}if ((hasJspFiles) && (!hasPrecompiledJsp)) {LOG.warn("JSP files were found in the current project but FindBugs requires their precompiled form. For more information on how to configure JSP precompilation : https://github.com/find-sec-bugs/find-sec-bugs/wiki/JSP-precompilation");}copyLibs();if (this.annotationsLib != null){findbugsProject.addAuxClasspathEntry(this.annotationsLib.getCanonicalPath());findbugsProject.addAuxClasspathEntry(this.jsr305Lib.getCanonicalPath());}findbugsProject.setCurrentWorkingDirectory(this.fileSystem.workDir());return findbugsProject;}private Iterable<File> getSourceFiles(){FilePredicates pred = this.fileSystem.predicates();return this.fileSystem.files(pred.and(new FilePredicate[] {pred.hasType(InputFile.Type.MAIN), pred.hasLanguage("java"), pred.not(pred.matchesPathPattern("**/package-info.java")) }));}private boolean hasSourceFiles(){FilePredicates pred = this.fileSystem.predicates();return this.fileSystem.hasFiles(pred.and(new FilePredicate[] {pred.hasType(InputFile.Type.MAIN), pred.hasLanguage("java"), pred.not(pred.matchesPathPattern("**/package-info.java")) }));}@VisibleForTestingFile saveIncludeConfigXml()throws IOException{StringWriter conf = new StringWriter();this.exporter.exportProfile(this.profile, conf);File file = new File(this.fileSystem.workDir(), "findbugs-include.xml");FileUtils.write(file, conf.toString(), "UTF-8");return file;}public static List<File> scanForAdditionalClasses(File folder)throws IOException{List<File> allFiles = new ArrayList();Queue<File> dirs = new LinkedList();dirs.add(folder);while (!dirs.isEmpty()){File dirPoll = (File)dirs.poll();if (dirPoll == null) {break;}for (File f : dirPoll.listFiles()) {if (f.isDirectory()) {dirs.add(f);} else if ((f.isFile()) && (f.getName().endsWith(".class"))) {allFiles.add(f);}}}return allFiles;}@VisibleForTestingList<File> getExcludesFilters(){List<File> result = Lists.newArrayList();PathResolver pathResolver = new PathResolver();String[] filters = this.settings.getStringArray("sonar.findbugs.excludesFilters");for (String excludesFilterPath : filters){excludesFilterPath = StringUtils.trim(excludesFilterPath);if (StringUtils.isNotBlank(excludesFilterPath)) {result.add(pathResolver.relativeFile(this.fileSystem.baseDir(), excludesFilterPath));}}return result;}public String getEffort(){return StringUtils.lowerCase(this.settings.getString("sonar.findbugs.effort"));}public String getConfidenceLevel(){return StringUtils.lowerCase(this.settings.getString("sonar.findbugs.confidenceLevel"));}public long getTimeout(){return this.settings.getLong("sonar.findbugs.timeout");}public void copyLibs(){if (this.jsr305Lib == null) {this.jsr305Lib = copyLib("/jsr305.jar");}if (this.annotationsLib == null) {this.annotationsLib = copyLib("/annotations.jar");}if (this.fbContrib == null) {this.fbContrib = copyLib("/fb-contrib.jar");}if (this.findSecBugs == null) {this.findSecBugs = copyLib("/findsecbugs-plugin.jar");}}public void stop(){if (this.jsr305Lib != null) {this.jsr305Lib.delete();}if (this.annotationsLib != null) {this.annotationsLib.delete();}if (this.fbContrib != null) {this.fbContrib.delete();}if (this.findSecBugs != null) {this.findSecBugs.delete();}}private File copyLib(String name){InputStream input = null;try{input = getClass().getResourceAsStream(name);File dir = new File(this.fileSystem.workDir(), "findbugs");FileUtils.forceMkdir(dir);File target = new File(dir, name);FileUtils.copyInputStreamToFile(input, target);return target;}catch (IOException e){throw new IllegalStateException("Fail to extract Findbugs dependency", e);}finally{IOUtils.closeQuietly(input);}}public File getFbContribJar(){return this.fbContrib;}public File getFindSecBugsJar(){return this.findSecBugs;}public static List<PropertyDefinition> getPropertyDefinitions(){String subCategory = "FindBugs";return ImmutableList.of(PropertyDefinition.builder("sonar.findbugs.effort").defaultValue("Default").category("java").subCategory(subCategory).name("Effort").description("Effort of the bug finders. Valid values are Min, Default and Max. Setting 'Max' increases precision but also increases memory consumption.").onQualifiers("TRK", new String[] { "BRC" }).build(), PropertyDefinition.builder("sonar.findbugs.timeout").defaultValue(Long.toString(600000L)).category("java").subCategory(subCategory).name("Timeout").description("Specifies the amount of time, in milliseconds, that FindBugs may run before it is assumed to be hung and is terminated. The default is 600,000 milliseconds, which is ten minutes.").onQualifiers("TRK", new String[] { "BRC" }).type(PropertyType.INTEGER).build(), PropertyDefinition.builder("sonar.findbugs.excludesFilters").category("java").subCategory(subCategory).name("Excludes Filters").description("Paths to findbugs filter-files with exclusions.").onQualifiers("TRK", new String[] { "BRC" }).multiValues(true).build(), PropertyDefinition.builder("sonar.findbugs.confidenceLevel").defaultValue("medium").category("java").subCategory(subCategory).name("Confidence Level").description("Specifies the confidence threshold (previously called \"priority\") for reporting issues. If set to \"low\", confidence is not used to filter bugs. If set to \"medium\" (the default), low confidence issues are supressed. If set to \"high\", only high confidence bugs are reported. ").onQualifiers("TRK", new String[] { "BRC" }).build());} }FindbugsExecutor 類這個類定義了該plugin 有執(zhí)行掃描功能,去創(chuàng)建線程執(zhí)行,在執(zhí)行之前去加載其他插件。其他plugin可能只提供規(guī)則,但是這個plugin可以具備掃描執(zhí)行能力。
@BatchSide 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);private static Map<String, Integer> priorityNameToValueMap = new HashMap();static{priorityNameToValueMap.put("high", Integer.valueOf(1));priorityNameToValueMap.put("medium", Integer.valueOf(2));priorityNameToValueMap.put("low", Integer.valueOf(3));priorityNameToValueMap.put("experimental", Integer.valueOf(4));}private static final Integer DEFAULT_PRIORITY = Integer.valueOf(2);private final FindbugsConfiguration configuration;public FindbugsExecutor(FindbugsConfiguration configuration){this.configuration = configuration;}@VisibleForTestingCollection<ReportedBug> execute(){return execute(true);}public Collection<ReportedBug> execute(boolean useAllPlugin){return execute(useAllPlugin, useAllPlugin);}public Collection<ReportedBug> execute(boolean useFbContrib, boolean useFindSecBugs){SecurityManager currentSecurityManager = System.getSecurityManager();ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(FindBugs2.class.getClassLoader());Locale initialLocale = Locale.getDefault();Locale.setDefault(Locale.ENGLISH);OutputStream xmlOutput = null;Collection<Plugin> customPlugins = null;ExecutorService executorService = Executors.newSingleThreadExecutor();try{FindBugs2 engine = new FindBugs2();Project project = this.configuration.getFindbugsProject();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().intValue());xmlBugReporter.setAddMessages(true);File xmlReport = this.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(this.configuration.getEffort());engine.setUserPreferences(userPreferences);engine.addFilter(this.configuration.saveIncludeConfigXml().getAbsolutePath(), true);for (Object localObject1 = this.configuration.getExcludesFilters().iterator(); ((Iterator)localObject1).hasNext();){File filterFile = (File)((Iterator)localObject1).next();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();executorService.submit(new FindbugsTask(engine)).get(this.configuration.getTimeout(), TimeUnit.MILLISECONDS);return toReportedBugs(xmlBugReporter.getBugCollection());}catch (TimeoutException e){throw new IllegalStateException("Can not execute Findbugs with a timeout threshold value of " + this.configuration.getTimeout() + " milliseconds", e);}catch (Exception e){throw new IllegalStateException("Can not execute Findbugs", e);}finally{System.setSecurityManager(currentSecurityManager);resetCustomPluginList(customPlugins);executorService.shutdown();IOUtils.closeQuietly(xmlOutput);Thread.currentThread().setContextClassLoader(initialClassLoader);Locale.setDefault(initialLocale);}}private static Collection<ReportedBug> toReportedBugs(BugCollection bugCollection){Collection<ReportedBug> bugs = new ArrayList();for (BugInstance bugInstance : bugCollection) {if (bugInstance.getPrimarySourceLineAnnotation() == null) {LOG.warn("No source line for " + bugInstance.getType());} else {bugs.add(new ReportedBug(bugInstance));}}return bugs;}private Integer determinePriorityThreshold(){Integer integer = (Integer)priorityNameToValueMap.get(this.configuration.getConfidenceLevel());if (integer == null) {integer = DEFAULT_PRIORITY;}return integer;}private static class FindbugsTaskimplements Callable<Object>{private final FindBugs2 engine;public FindbugsTask(FindBugs2 engine){this.engine = engine;}public Object call(){try{this.engine.execute();return null;}catch (InterruptedException|IOException e){throw Throwables.propagate(e);}finally{this.engine.dispose();}}}private Collection<Plugin> loadFindbugsPlugins(boolean useFbContrib, boolean useFindSecBugs){ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();List<String> pluginJarPathList = Lists.newArrayList();URL url;try{Enumeration<URL> urls = contextClassLoader.getResources("findbugs.xml");while (urls.hasMoreElements()){url = (URL)urls.nextElement();pluginJarPathList.add(normalizeUrl(url));}if ((useFbContrib) && (this.configuration.getFbContribJar() != null)) {pluginJarPathList.add(this.configuration.getFbContribJar().getAbsolutePath());}if ((useFindSecBugs) && (this.configuration.getFindSecBugsJar() != null)) {pluginJarPathList.add(this.configuration.getFindSecBugsJar().getAbsolutePath());}}catch (IOException e){throw new IllegalStateException(e);}catch (URISyntaxException e){throw new IllegalStateException(e);}List<Plugin> customPluginList = Lists.newArrayList();for (String path : pluginJarPathList) {try{Plugin plugin = Plugin.addCustomPlugin(new File(path).toURI(), contextClassLoader);if (plugin != null){customPluginList.add(plugin);LOG.info("Loading findbugs plugin: " + path);}}catch (PluginException e){LOG.warn("Failed to load plugin for custom detector: " + path);LOG.debug("Cause of failure", e);}catch (DuplicatePluginIdException e){if (!"edu.umd.cs.findbugs.plugins.core".equals(e.getPluginId())) {LOG.debug("Plugin already loaded: exception ignored: " + e.getMessage(), e);}}}return customPluginList;}private static String normalizeUrl(URL url)throws URISyntaxException{return StringUtils.removeStart(StringUtils.substringBefore(url.toURI().getSchemeSpecificPart(), "!"), "file:");}private static void disableUpdateChecksOnEveryPlugin(){for (Plugin plugin : ) {plugin.setMyGlobalOption("noUpdateChecks", "true");}}private static void resetCustomPluginList(Collection<Plugin> customPlugins){if (customPlugins != null) {for (Plugin plugin : customPlugins) {Plugin.removeCustomPlugin(plugin);}}} }findbugs-result.xml 掃描相關(guān)的類:
class FindbugsXmlReportParser {private final File findbugsXmlReport;private final String findbugsXmlReportPath;public FindbugsXmlReportParser(File findbugsXmlReport){this.findbugsXmlReport = findbugsXmlReport;this.findbugsXmlReportPath = findbugsXmlReport.getAbsolutePath();if (!findbugsXmlReport.exists()) {throw new IllegalStateException("The findbugs XML report can't be found at '" + this.findbugsXmlReportPath + "'");}}public List<XmlBugInstance> getBugInstances(){List<XmlBugInstance> result = Lists.newArrayList();try{SMInputFactory inf = new SMInputFactory(XMLInputFactory.newInstance());SMInputCursor cursor = inf.rootElementCursor(this.findbugsXmlReport).advance();SMInputCursor bugInstanceCursor = cursor.childElementCursor("BugInstance").advance();while (bugInstanceCursor.asEvent() != null){XmlBugInstance xmlBugInstance = new XmlBugInstance();xmlBugInstance.type = bugInstanceCursor.getAttrValue("type");xmlBugInstance.longMessage = "";result.add(xmlBugInstance);ImmutableList.Builder<XmlSourceLineAnnotation> lines = ImmutableList.builder();SMInputCursor bugInstanceChildCursor = bugInstanceCursor.childElementCursor().advance();while (bugInstanceChildCursor.asEvent() != null){String nodeName = bugInstanceChildCursor.getLocalName();if ("LongMessage".equals(nodeName)){xmlBugInstance.longMessage = bugInstanceChildCursor.collectDescendantText();}else if ("SourceLine".equals(nodeName)){XmlSourceLineAnnotation xmlSourceLineAnnotation = new XmlSourceLineAnnotation();xmlSourceLineAnnotation.parseStart(bugInstanceChildCursor.getAttrValue("start"));xmlSourceLineAnnotation.parseEnd(bugInstanceChildCursor.getAttrValue("end"));xmlSourceLineAnnotation.parsePrimary(bugInstanceChildCursor.getAttrValue("primary"));xmlSourceLineAnnotation.className = bugInstanceChildCursor.getAttrValue("classname");lines.add(xmlSourceLineAnnotation);}bugInstanceChildCursor.advance();}xmlBugInstance.sourceLines = lines.build();bugInstanceCursor.advance();}cursor.getStreamReader().closeCompletely();}catch (XMLStreamException e){throw new IllegalStateException("Unable to parse the Findbugs XML Report '" + this.findbugsXmlReportPath + "'", e);}return result;}public static class XmlBugInstance{private String type;private String longMessage;private List<FindbugsXmlReportParser.XmlSourceLineAnnotation> sourceLines;public String getType(){return this.type;}public String getLongMessage(){return this.longMessage;}@CheckForNullpublic FindbugsXmlReportParser.XmlSourceLineAnnotation getPrimarySourceLine(){for (FindbugsXmlReportParser.XmlSourceLineAnnotation sourceLine : this.sourceLines) {if (sourceLine.isPrimary()) {return sourceLine;}}return this.sourceLines.isEmpty() ? null : (FindbugsXmlReportParser.XmlSourceLineAnnotation)this.sourceLines.get(0);}}public static class XmlSourceLineAnnotation{private boolean primary;private Integer start;private Integer end;@VisibleForTestingprotected String className;public void parseStart(String attrValue){try{this.start = Integer.valueOf(Integer.parseInt(attrValue));}catch (NumberFormatException e){this.start = null;}}public void parseEnd(String attrValue){try{this.end = Integer.valueOf(Integer.parseInt(attrValue));}catch (NumberFormatException e){this.end = null;}}public void parsePrimary(String attrValue){this.primary = Boolean.parseBoolean(attrValue);}public boolean isPrimary(){return this.primary;}public Integer getStart(){return this.start;}public Integer getEnd(){return this.end;}public String getClassName(){return this.className;}public String getSonarJavaFileKey(){if (this.className.indexOf('$') > -1) {return this.className.substring(0, this.className.indexOf('$'));}return this.className;}} }總結(jié)
以上是生活随笔為你收集整理的sonar findbugs plugin源码研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 委托(Delegate)简介
- 下一篇: 马斯克:人形机器人Optimus将使用F