首页 > 其他分享 >自定义sonar插件

自定义sonar插件

时间:2023-08-04 10:32:21浏览次数:44  
标签:插件 java 自定义 规则 api org sonar import

第一步:创建插件应用

mvn archetype:generate 
    -DgroupId=com.yy  
    -DartifactId=sonar-yy-plugin 
    -Dpackage=com.yy 
    -Dversion=1.0.0-SNAPSHOT 
    -DarchetypeGroupId=org.apache.maven.archetypes 
    -DarchetypeArtifactId=maven-archetype-quickstart 
    -DarchetypeVersion=1.4  
    -DinteractiveMode=false


第二步:配置pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.yy</groupId>
  <artifactId>sonar-yy-plugin</artifactId>
  <!-- 注意这里的packaging-->
  <packaging>sonar-plugin</packaging>
  <version>1.0</version>
  <name>sonar-yy-plugin</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <sonar.version>9.4.0.54424</sonar.version>
    <sonarqube.version>9.9.0.229</sonarqube.version>
    <sonarjava.version>7.16.0.30901</sonarjava.version>
    <analyzer.commons.version>2.5.0.1358</analyzer.commons.version>
  </properties>
  <dependencies>
    <!-- groupId has changed to 'org.sonarsource.api.plugin' starting on version 9.5 -->
    <dependency>
      <groupId>org.sonarsource.sonarqube</groupId>
      <artifactId>sonar-plugin-api</artifactId>
      <version>${sonar.version}</version>
      <scope>provided</scope>
    </dependency>
    
    <!--用于基于java的自定义规则-->
    <dependency>
      <groupId>org.sonarsource.java</groupId>
      <artifactId>sonar-java-plugin</artifactId>
      <type>sonar-plugin</type>
      <version>4.7.1.9272</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.sonarsource.analyzer-commons</groupId>
      <artifactId>sonar-analyzer-commons</artifactId>
      <version>${analyzer.commons.version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId>
        <artifactId>sonar-packaging-maven-plugin</artifactId>
        <version>1.17</version>
        <extensions>true</extensions>
        <configuration>
          <pluginDescription>test</pluginDescription>
          <pluginKey>java-custom</pluginKey>
          <pluginName>Java Custom Rules</pluginName>
          <!-- 重要:这个类就是下文定义的插件类 -->
          <pluginClass>com.yy.YYPlugin</pluginClass>
          <pluginDescription>how to write sonar plugin</pluginDescription>
          <sonarLintSupported>true</sonarLintSupported>
          <sonarQubeMinVersion>${sonarqube.version}</sonarQubeMinVersion>
          <requirePlugins>java:${sonarjava.version}</requirePlugins>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>


第三步:开发规则

package com.yy.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.Tree;

import java.util.Collections;
import java.util.List;

/**
 * 规则:方法名称起始字母小写.
 * 这里的key会起到关键字作用,后面要创建的文件名与其一致。
 */
@Rule(key = "MethodNameStartLowerCase")
public class MethodNameStartLowerCaseRule extends IssuableSubscriptionVisitor {

    @Override
    public List<Tree.Kind> nodesToVisit() {
        return Collections.singletonList(Tree.Kind.METHOD);
    }

    @Override
    public void visitNode(Tree tree) {
        MethodTree method = (MethodTree) tree;
        //获取方法名
        String methodName = method.simpleName().name() ;
        if(!Character.isLowerCase(methodName.charAt(0))){
            //规则未通过,生成Issue
            reportIssue(method.simpleName(), "方法首字母必须小写");
        }
        super.visitNode(tree);
    }
}


package com.yy.checks;

import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.plugins.java.api.JavaFileScanner;
import org.sonar.plugins.java.api.JavaFileScannerContext;
import org.sonar.plugins.java.api.tree.*;

import java.util.List;

/**
 * 规则:避免注解
 */
@Rule(key = "AvoidAnnotation")
public class AvoidAnnotationRule extends BaseTreeVisitor implements JavaFileScanner {

  private static final String DEFAULT_VALUE = "Inject";

  private JavaFileScannerContext context;

  /**
   * Name of the annotation to avoid. Value can be set by users in Quality profiles.
   * The key
   */
  @RuleProperty(
    defaultValue = DEFAULT_VALUE,
    description = "Name of the annotation to avoid, without the prefix @, for instance 'Override'")
  protected String name;

  @Override
  public void scanFile(JavaFileScannerContext context) {
    this.context = context;
    scan(context.getTree());
  }

  @Override
  public void visitMethod(MethodTree tree) {
    List<AnnotationTree> annotations = tree.modifiers().annotations();
    for (AnnotationTree annotationTree : annotations) {
      TypeTree annotationType = annotationTree.annotationType();
      if (annotationType.is(Tree.Kind.IDENTIFIER)) {
        IdentifierTree identifier = (IdentifierTree) annotationType;
        if (identifier.name().equals(name)) {
          context.reportIssue(this, identifier, String.format("Avoid using annotation @%s", name));
        }
      }
    }

    // The call to the super implementation allows to continue the visit of the AST.
    // Be careful to always call this method to visit every node of the tree.
    super.visitMethod(tree);
  }
}


第四步:创建规则元数据

在resources目录下创建“rules/java”目录,并在里面创建MethodNameStartLowerCase.html、MethodNameStartLowerCase.json、AvoidAnnotation.html和AvoidAnnotation.json文件。

1、MethodNameStartLowerCase.html

等同帮助文档,以帮助开发人员解决问题。

<p>该规则检测方法的名称是否已小写字母开头</p>
<h2>不符合规范示例代码</h2>
<pre>
class MyClass {
  int DoSomething(int a) { // 方法名称首字母为大写
    return 42;
  }
}
</pre>
<h2>符合规范示例代码</h2>
<pre>
class MyClass {
  int doSomething(int a) { // 方法名称首字母为小写
    return 42;
  }
}
</pre>

2、MethodNameStartLowerCase.json

规则的配置信息

{
  "title": "Method names start with lowercase letters",
  "type": "CODE_SMELL",
  "status": "ready",
  "tags": [
    "method",
    "bugs"
  ],
  "defaultSeverity": "Critical"
}

3、AvoidAnnotation.html

<p>This rule detects usage of configured annotation</p>
<h2>Noncompliant Code Example</h2>
<pre>
TO DO 
</pre>
<h2>Compliant Solution</h2>
<pre>
TO DO 
</pre>

4、AvoidAnnotation.json

{
  "title": "Title of AvoidAnnotation",
  "type": "CODE_SMELL",
  "status": "ready",
  "remediation": {
    "func": "Constant\/Issue",
    "constantCost": "5min"
  },
  "tags": [
    "pitfall"
  ],
  "defaultSeverity": "Minor"
}


第五步:激活规则

这里的激活指的是将哪些规则收集应用

package com.yy;

import com.yy.checks.AvoidAnnotationRule;
import com.yy.checks.MethodNameStartLowerCaseRule;
import com.yy.checks.NoIfStatementInTestsRule;
import org.sonar.plugins.java.api.JavaCheck;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 规则集。 统一在这里地方将规则收集,是因为后面有两个地方会用到这些规则
 */
public final class RulesList {
    private RulesList() {}

    public static List<Class<? extends JavaCheck>> getChecks() {
        List<Class<? extends JavaCheck>> checks = new ArrayList<>();
        checks.addAll(getJavaChecks());
        checks.addAll(getJavaTestChecks());
        return Collections.unmodifiableList(checks);
    }

    /**
     * These rules are going to target MAIN code only
     */
    public static List<Class<? extends JavaCheck>> getJavaChecks() {
        return Collections.unmodifiableList(Arrays.asList(
                MethodNameStartLowerCaseRule.class,
                AvoidAnnotationRule.class));
    }

    /**
     * These rules are going to target TEST code only
     */
    public static List<Class<? extends JavaCheck>> getJavaTestChecks() {
        return Collections.unmodifiableList(Arrays.asList(
                NoIfStatementInTestsRule.class));
    }
}


第六步:定义编码规则

到此为止,上面的过程创建了规则运行代码、创建了规则的元数据、将规则进行了分类存储,那么接下来便要将这些规则集成到sonar中去进行应用。

package com.yy;

import org.sonar.api.SonarRuntime;
import org.sonar.api.config.Configuration;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonarsource.analyzer.commons.RuleMetadataLoader;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;

/**
 * 定义相同存储库的一些编码规则。例如,Java Findbugs插件提供了这个扩展点的实现,以便定义它所支持的规则。
 * 在服务器规则存储库中声明规则元数据。这允许在“规则”页面中列出规则。
 * 说明:RulesDefinition接口取代了已弃用的类org.sonar.api.rules.RuleRepository
 */
public class MyRulesDefinition implements RulesDefinition {

    /**
     * 核心组件org.sonar.api.config.Configuration提供对配置的访问。
     * 它处理默认值和值的解密。它可用于所有栈(扫描器,web服务器,计算引擎)
     */
    private final Configuration config;
    private final SonarRuntime runtime;

    // Add the rule keys of the rules which need to be considered as template-rules
    private static final Set<String> RULE_TEMPLATES_KEY = Collections.emptySet();

    //规则的元数据和json文件所在路径
    private static final String RESOURCE_BASE_PATH = "rules/java";
    //规则所属的存储库关键字
    public static final String REPOSITORY_KEY = "custom-repo";
    //规则所属的存储库名称
    public static final String REPOSITORY_NAME = "My Custom Repository";

    public MyRulesDefinition(Configuration config,SonarRuntime runtime){
        this.config = config ;
        this.runtime = runtime ;
    }

    /**
     * 此方法在服务器启动时执行
     */
    @Override
    public void define(Context context) {
        //实例化一个新仓库,指定这个规则适应的编程语言
        final NewRepository repository = context.createRepository(REPOSITORY_KEY, "java")
            .setName(REPOSITORY_NAME);
        //通过规则元数据进行加载的Loader
        RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_BASE_PATH, runtime);
        //添加规则,同时会检查是否有添加了@Rule注解,以及根据key解析对应的html和json文件
        ruleMetadataLoader.addRulesByAnnotatedClass(repository, new ArrayList<>(RulesList.getChecks()));

        setTemplates(repository);

        repository.done();
    }

    private static void setTemplates(NewRepository repository) {
        RULE_TEMPLATES_KEY.stream()
                .map(repository::rule)
                .filter(Objects::nonNull)
                .forEach(rule -> rule.setTemplate(true));
    }
}


第七步:注册规则

因为定义的规则依赖于Java API的SonarSource Analyzer,所以还需要告诉父插件必须检索一些新规则。

package com.yy;

import org.sonar.plugins.java.api.CheckRegistrar;
import org.sonarsource.api.sonarlint.SonarLintSide;

/**
 * 因为我们的规则依赖于Java API的SonarSource Analyzer,
 * 所以还需要告诉父Java插件必须检索一些新规则。
 */
@SonarLintSide
public class MyCheckRegistrar implements CheckRegistrar {
    /**
     * 在分析期间调用此方法以获取用于实例化检查的类。
     * @param registrarContext java插件将使用这个上下文来检索要检查的类
     */
    @Override
    public void register(RegistrarContext registrarContext) {
        registrarContext
            .registerClassesForRepository(MyRulesDefinition.REPOSITORY_KEY
                                          ,RulesList.getJavaChecks()
                                          ,RulesList.getJavaTestChecks());
    }
}


第八步:定义插件

插件将扩展注入SonarQube的入口点

package com.yy;

import com.yy.sensors.CustomSensor;
import com.yy.sensors.Foo;
import org.sonar.api.Plugin;
import org.sonar.api.config.PropertyDefinition;

/**
 * SonarQube为它的三个技术栈提供了扩展点:
 * - Scanner : 运行源代码分析
 * - Compute Engine:它整合扫描器的输出,例如
 *  1)计算二级指标,如评级
 *  2)聚合度量(例如项目的代码行数=所有文件的代码行数之和)
 *  3)将新问题分配给开发人员
 *  4)将所有内容持久化到数据存储中
 * - Web application
 * 扩展点不是用来添加新特性的,而是用来完善现有特性的。
 * 从技术上讲,它们是由Java接口或带有@ExtensionPoint注释的抽象类定义的契约。
 * 插件提供的扩展点(命名的扩展)的实现必须在其入口点类中声明,
 * 该入口点类实现org.sonar.api.Plugin接口并在pom.xml中引用
 *
 * 由以下三个注解分别标注应该被执行的环境(栈)
 * 1) @ScannerSide:扫描仪运行时
 * 2) @ServerSide:web服务器
 * 3) @ComputeEngineSide:计算引擎
 *
 * 例如,一个scanner sensor只能在scanner runtime被实例化和执行,
 * 而不在web服务器(web server)和计算引擎中(Compute Engine)
 */
public class YYPlugin implements Plugin {

    @Override
    public void define(Context context) {
        //插件可以定义自己的属性,以便可以从web管理控制台配置它们。
        //必须使用扩展点org.sonar.api.config.PropertyDefinition
        //也可以在扩展上使用注释org.sonar.api.config.PropertyDefinition来声明属性。
        context.addExtension(
                PropertyDefinition.builder("sonar.my.property")
                        .name("My Property")
                        .description("This is the description displayed in web admin console")
                        .defaultValue("42")
                        .build()
        );
        //添加扩展
        context.addExtensions(MyRulesDefinition.class
                              ,MyCheckRegistrar.class) ;
    }
}


第九步:注册插件

将应用进行打包,得到插件的jar包,sonar-yy-plugin-1.0.jar

拷贝该jar包到Sonar Qube安装路径下的“extensions\plugins”中,然后重启Sonar即可


第十步:查看规则

打开sonar页面,切换到“代码规则”菜单,输入规则的关键字进行搜索,如下所示

自定义sonar插件_ide

点击右侧的规则进入

自定义sonar插件_maven_02

点击激活规则

自定义sonar插件_java_03

自定义sonar插件_maven_04



更多自定义规则说明请移步:https://github.com/SonarSource/sonar-java/blob/master/docs/CUSTOM_RULES_101.md


补充说明

上面在定义规则的时候使用了html和json文件来描述插件的信息,除此以外,我们还可以直接在代码中进行描述,即“以编程的方式定义规则”,例如:

package com.yy;

import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.rule.RuleParamType;
import org.sonar.api.server.rule.RulesDefinition;


/**
 * 以编程方式定义规则
 */
public class MyCodeRulesDefinition implements RulesDefinition {

    @Override
    public void define(Context context) {
        // 规则库
        NewRepository repository = context.createRepository("my_js", "js")
                .setName("My Javascript Analyzer");
        // 以编程方式定义规则. 注意,规则可以从文件中加载(JSON, XML,…)
        NewRule x1Rule = repository.createRule("x1")
                .setName("No empty line")
                .setHtmlDescription("Generate an issue on empty lines")
                // optional tags
                .setTags("style", "stupid")
                // optional status. Default value is READY.
                .setStatus(RuleStatus.BETA)
                // default severity when the rule is activated on a Quality profile. Default value is MAJOR.
                .setSeverity(Severity.MINOR)
                // optional type for SonarQube Quality Model. Default is RuleType.CODE_SMELL.
                .setType(RuleType.BUG) ;

        //用于计算问题修复成本
        x1Rule.setDebtRemediationFunction(
            x1Rule.debtRemediationFunctions()
            .linearWithOffset("1h", "30min"));
        //创建参数
        x1Rule.createParam("acceptWhitespace")
                .setDefaultValue("false")
                .setType(RuleParamType.BOOLEAN)
                .setDescription("Accept whitespaces on the line");
        // don't forget to call done() to finalize the definition
        repository.done();
    }
}






标签:插件,java,自定义,规则,api,org,sonar,import
From: https://blog.51cto.com/dengshuangfu/6957850

相关文章

  • 金蝶云不重启iis开发插件
    1、打开WebSite\App_Data\Common.config,修改appSettings,设置IsEnablePlugInHotUpdate为true,重启IIS;若appSetting中没有IsEnablePlugInHotUpdate,可自行添加;  <!--是否启用插件热更新(开发时启用,正式服务器必须设为false,以免影响性能)--><addkey="IsEnablePlugInHotUpdate"va......
  • 重磅特性 - SpreadJS推出新插件甘特图,预览版下载体验中
    摘要:本文由葡萄城技术团队于博客园原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。甘特图对于业务场景中的工程项目管理、预算执行、生产计划等都能将原有的表格数据,转变为直观的甘特图模式。作为纯前端表格控件SpreadJS......
  • jmeter自定义参数使用固定前缀
    使用元件随机变量参数填写方式若随机数取值为123生成值为:name_000000123若随机数取值为12312321生成值为:name_012312321......
  • ELK elasticsearch 插件管理
    插件能额外扩展elasticsearch功能,提供各类功能等等。有三种类型的插件:java插件只包含JAR文件,必须在集群中每个节点上安装而且需要重启才能使插件生效。网站插件这类插件包含静态web内容,如js、css、html等等,可以直接从elasticsearch服务,如head插件。只需在一个节点上安装,不需要重启......
  • 《Ext详解与实践》节选:自定义单元格的显示格式
    有时候,需要在Grid中显示一个链接,或根据数字的大小显示不同的颜色,又或者使用固定格式显示记录编号等,这就需要自定义单元格的显示格式。在Ext的Grid可以很容易的实现自定义单元格的显示格式。在定义Grid的ColumnModel的时候,在需要自定义显示格式的列中加入参数renderer,该参数指向一个......
  • Spring Data JPA 的开发神级IDEA插件 -- JPA Buddy
    1.概述JPABuddy是一个广泛使用的IntelliJIDEA插件,面向使用JPA数据模型和相关技术(如SpringDataJPA,DB版本控制工具(Flyway,Liquibase),MapStruct等)的新手和有经验的开发人员。该插件提供了可视化设计器、代码生成和其他检查,这些检查应根据JPA的最佳实践简化开发并改进代码。该插......
  • IDEA超强XSD文件编辑插件-XSD / WSDL Visualizer
    前言XSD/WSDLVisualizer可以简化XML架构定义(XSD)和WSDL文件编辑过程;通过使用与IntelliJ无缝集成的可视化编辑器,转换处理XSD和WSDL文件的方式。告别导航复杂和难以阅读的代码的挫败感,迎接流线型和直观的体验。插件安装在线安装IntelliJIDEA可通过在线安装的方式,安装时......
  • IDEA必备插件之Sequence Diagram,你GET了么?
    前言“无论是快速了解业务流程,还是快速的熟悉系统的业务代码逻辑,以及各个类和方法等的调用关系,时序图无疑是其中一种不可获取的简便快捷的方式。一起来了解下,IDEA如何快速生成时序图吧。”工作中,经常需要绘制时序图说明业务流程的设计走向,而逻辑较复杂的时序图,若是单纯的通过人......
  • protoc-gen-doc 自定义模板规则详解
    protoc-gen-doc自定义模板规则详解配套演示工程此项目中所用proto文件位于./proto目录下,来源于官方proto示例此项目中所列所有模板case文件位于./tmpl目录下此教程均基于markdown文本演示前言最近有通过proto文件生成其接口文档的需求,而protoc-gen-doc所生成......
  • PlayWright(二十三)- allure插件(二)
    在上文中,我们认识了allure插件,并且也成功使用了,但是感觉少点东西,所以我们再深入挖掘下allure的功能  1.allure增加测试用例详情 1、导入allure模块2、在每条用例函数前加上@allure.title("标题内容")3、正常执行生成allure报告执行结果:2.allure增加测试用例描述用......