1 Sonar简介
SonarQube是一个非常流行和强大的静态代码检查工具。它可以针对很多语言进行分析,比如Java、C#、JavaScript、PL/SQL等。SonarQube中默认提供了很多代码检查规则,但是有时候我们需要根据自己的需求编写规则,这篇文章会涵盖如何编写自定义规则的各个步骤。
2 环境准备
- JDK 1.8
- Intellij Idea
- Maven 3.x或更高
- SonarQube LTS 5.6
- Sonar-runner-dist 2.4
3 开发步骤
3.1 创建Plugin项目
首先创建一个SonarPlugin项目,建议从SonarSource官方github站点下载模板项目,这样不至于遗漏配置。下载的地址是:下载链接
3.2 引入Pom依赖
将模板项目中的pom文件修改为自己项目对应的名称,比如groupId、artifactId、version、name、description,其他的不必修改。
1 | <?xml version="1.0" encoding="UTF-8"?> |
3.3 编写具体的Rule
一个自定义规则的实现,首先在规则类前面使用@Rule注解声明规则的相关信息,比如key、name、description、priority、tag。其次要继承IssuableSubscriptionVisitor或者继承BaseTreeVisitor并实现JavaFileScanner接口,这两种实现方式存在一些差异,IssuableSubscriptionVisitor是一个抽象类,继承自SubscriptionVisitor,而SubscriptionVisitor则实现了JavaFileScanner接口;而BaseTreeVisitor则是实现了TreeVisitor接口。相比来说,JavaFileScanner更偏底层一些,直接是面向Java文件的分析,而TreeVisitor则是针对于Java文件的语法结构了,比如类、防止、赋值语句、catch块之类的了。一般情况下,推荐使用继承IssuableSubscriptionVisitor的方式,因为这个类的封装层次更高,更简便。在规则类中,你只需要重载nodesToVisit和visitNode方法就可以了。
nodesToVisit方法是说明该规则做检查的类型集合,比如METHOD_INVOCATION(方法调用)、CATCH(catch块)、MEMBER_SELECT(属性选择)等。比如下面的代码,就是对源码中的方法调用做规则检查。
visitNode是自定义规则中的核心部分,你所要实现的代码检查逻辑就是在这个方法中完成的。
1 | import com.google.common.collect.ImmutableList; |
当然如果一些复杂的业务规则,可能你不得不实现JavaFileScanner接口了。如下面的代码所示:
1 | import com.qunar.sonar.java.util.QualifiedNameUtil; |
3.4 单元测试
规则检查代码写完之后,就可以针对这个规则进行单元测试了。单元测试的方法比较简单,就是使用JUnit框架编写test case,针对特定的目标文件,调用Sonar提供的JavaCheckVerifier.verify方法进行测试,示例代码如下:
1 | import com.qunar.flight.sonar.java.checks.DisallowedClassCheck; |
如果目标文件存在问题,那么控制台会打印出问题在目标文件中的位置:1
2
3
4
5
6
7
8
9java.lang.AssertionError: Unexpected at [4]
at org.sonar.java.checks.verifier.CheckVerifier.assertMultipleIssue(CheckVerifier.java:209)
at org.sonar.java.checks.verifier.CheckVerifier.checkIssues(CheckVerifier.java:181)
at org.sonar.java.checks.verifier.JavaCheckVerifier.scanFile(JavaCheckVerifier.java:274)
at org.sonar.java.checks.verifier.JavaCheckVerifier.scanFile(JavaCheckVerifier.java:256)
at org.sonar.java.checks.verifier.JavaCheckVerifier.scanFile(JavaCheckVerifier.java:222)
at org.sonar.java.checks.verifier.JavaCheckVerifier.verify(JavaCheckVerifier.java:105)
at com.qunar.flight.sonar.java.checks.test.DisallowedClassCheckTest.test(DisallowedClassCheckTest.java:10)
如果目标文件中不存在任何问题,会提示“java.lang.IllegalStateException: At least one issue expected”,意思就是目标文件中需要至少存在一个规则所要检查的问题。
3.5 编写RulesList
单元测试完成之后,就可以往RuleList的集合中中添加自定义规则规则了。这个RuleList只是一个简单的集合类,包含了所有你需要注册到Sonar中的规则:
1 | import com.google.common.collect.ImmutableList; |
3.6 编写CustomRulesDefinition
CustomRulesDefinition可以理解为自定义规则集的元数据,在这个类中,首先要声明自定义规则集的repository,另外还需要将相应的规则集加入到前面声明的repository中。
1 | mport com.google.common.annotations.VisibleForTesting; |
3.7 编写CustomJavaFileCheckRegistrar
CustomJavaFileCheckRegistrar要实现CheckRegistrar接口。CustomJavaFileCheckRegistrar类的作用是注册代码扫描时需要的规则类。
1 | import java.util.List; |
3.8 编写CustomJavaRulesPlugin
这个类是Sonar插件的入口,它继承了org.sonar.api.SonarPlugin类,包含了SonarQube启动时需要实例化的server扩展(CustomRulesDefinition)和代码分析时需要实例化的批量扩展(JavaFileCheckRegistrar)。
这个类基本不需要修改(除包名外),按照以下内容copy一份即可。
1 | import org.sonar.api.Plugin; |
至此,一个Sonar自定义插件所需的代码工作就结束了。剩下的就是进行整合性的测试,毕竟单元测试通过不代表插件不会对现有系统造成影响,有时候插件中的错误会导致SonarQube启动失败的。
5 整合测试
通过mvn package命令编译、打包,形成jar文件。将jar文件上传到SonarQube extensions/plugins目录下,重启SonarQube服务即可完成Sonar自定义规则插件的部署。部署结束,通过SonarQube bin目录下的脚本重启SonarQube,如果SonarQube启动成功,说明插件没问题,整合测试成功,你可以在sonar的rules模块中,搜索到自定义的规则了。
6 启用规则
插件部署到SonarQube之后,默认是不启用的,需要在SonarQube的管理界面中启用规则,才能让此规则在代码检查过程中生效。
至此,Sonar插件开发的流程就介绍完了。如前面所言,SonarQube是个强大的代码检查工具,一篇文章是无法介绍完的,尤其Sonar的语法树部分,涵盖了各种语言各种语法的方方面面,大家可以通过Sonar的官方文档了解更多的细节。