SonarQube插件开发自定义规则(1)简易demo

时间:2023-01-31 03:27:52

1、maven依赖

本开发教程适用于sonarqube5.x、6.x。

<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>wxtx</groupId>
    <artifactId>TestSonar</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>sonar-plugin</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <sonar.version>6.3</sonar.version>
        <!-- this 6.3 is only required to be compliant with SonarLint and it is required even if you just want to be compliant with SonarQube 5.6 -->
        <java.plugin.version>4.7.1.9272</java.plugin.version>
        <sslr.version>1.21</sslr.version>
        <gson.version>2.6.2</gson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.sonarsource.sonarqube</groupId>
            <artifactId>sonar-plugin-api</artifactId>
            <version>${sonar.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.sonarsource.java</groupId>
            <artifactId>sonar-java-plugin</artifactId>
            <type>sonar-plugin</type>
            <version>${java.plugin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.sonarsource.java</groupId>
            <artifactId>java-frontend</artifactId>
            <version>${java.plugin.version}</version>
        </dependency>

        <dependency>
            <groupId>org.sonarsource.sslr-squid-bridge</groupId>
            <artifactId>sslr-squid-bridge</artifactId>
            <version>2.6.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.codehaus.sonar.sslr</groupId>
                    <artifactId>sslr-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.codehaus.sonar</groupId>
                    <artifactId>sonar-plugin-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.codehaus.sonar.sslr</groupId>
                    <artifactId>sslr-xpath</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>jcl-over-slf4j</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.sonarsource.java</groupId>
            <artifactId>java-checks-testkit</artifactId>
            <version>${java.plugin.version}</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>${gson.version}</version>
        </dependency>


        <dependency>
            <groupId>org.sonarsource.sslr</groupId>
            <artifactId>sslr-testing-harness</artifactId>
            <version>${sslr.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.6.1</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.30</version>
        </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>wxtx.com.MySonarPlugin</pluginClass>
                    <sonarLintSupported>true</sonarLintSupported>
                    <sonarQubeMinVersion>5.6</sonarQubeMinVersion> <!-- allow to depend on API 6.x but run on LTS -->
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2、制定规则

/** * @author 孙泽明 * 抽象类命名使用TXAbstract或TXBase开头 */
@Rule(
    // 规则id
    key = "TXAbstractClassNameCheck",
    // 规则名称
    name = "抽象类命名使用TXAbstract或TXBase开头",
    // 规则介绍
    description = "抽象类命名使用TXAbstract或TXBase开头",
    // 规则标签
    tags = {"wxtx-java"},
    // 规则级别
    priority = Priority.MINOR)
@SqaleSubCharacteristic(RulesDefinition.SubCharacteristics.ARCHITECTURE_CHANGEABILITY)
// 纠正错误所需时间
@SqaleConstantRemediation("10min")
public class TXAbstractClassNameCheck extends BaseTreeVisitor implements JavaFileScanner {
    private JavaFileScannerContext context;
    public void scanFile(JavaFileScannerContext context) {
        this.context = context;
        scan(context.getTree());
    }

    @Override
    public void visitClass(ClassTree tree) {
        if(tree == null || tree.simpleName() == null){
            super.visitClass(tree);
            return;
        }

        String className = tree.simpleName().name();
        boolean isAbstract = tree.symbol().isAbstract();
        if(isAbstract && isNameIll(className)){
            context.reportIssue(this, tree, "抽象类命名使用TXAbstract或TXBase开头");
        }
        super.visitClass(tree);
    }

    private boolean isNameIll(String className){
        return !className.startsWith("TXAbstract") && !className.startsWith("TXBase");
    }
}

3、注册规则

3.1、自定义插件入口

public class TXSonarPlugin implements Plugin {
    public void define(Context context) {
        context.addExtension(TXJavaRulesDefinition.class);
        context.addExtension(TXJavaFileCheckRegistrar.class);
    }
}

3.2、server extensions
在sonarqube server启动时实例化,实现org.sonar.api.server.rule.RulesDefinition接口

public class TXJavaRulesDefinition implements RulesDefinition {
    public static final String REPOSITORY_KEY = "TXRepo";
    public void define(Context context) {
        NewRepository repository = context.createRepository(REPOSITORY_KEY,Java.KEY);
        repository.setName("Java编码规范");
        AnnotationBasedRulesDefinition.load(repository, "java",TXRulesList.getChecks());
        repository.done();
    }
}

3.3、batch extensions
在分析代码的时候实例化,实现org.sonar.plugins.java.api.CheckRegistrar接口

public class TXJavaFileCheckRegistrar implements CheckRegistrar {
     public void register(RegistrarContext registrarContext) {
            registrarContext.registerClassesForRepository(TXJavaRulesDefinition.REPOSITORY_KEY,
                    Arrays.asList(checkClasses()), Arrays.asList(testCheckClasses()));
        }

        public static Class<? extends JavaCheck>[] checkClasses() {
            new Class[] {TXAbstractClassNameCheck.class};
        }
        @SuppressWarnings("unchecked")
        public static Class<? extends JavaCheck>[] testCheckClasses() {
            return new Class[] {};
        }
}
public class TXRulesList {

    private TXRulesList() {
    }
    public static List<Class> getChecks() {
        return ImmutableList.<Class>builder().addAll(getJavaChecks()).addAll(getJavaTestChecks()).build();
    }
    public static List<Class<? extends JavaCheck>> getJavaChecks() {
        return ImmutableList.<Class<? extends JavaCheck>>builder()
            .add(TXAbstractClassNameCheck.class).build();
    }
    public static List<Class<? extends JavaCheck>> getJavaTestChecks() {
        return ImmutableList.<Class<? extends JavaCheck>>builder().build();
    }
}

4、输出

maven执行命令mvn clean install输出jar包,其中修改MANIFEST.MF的Plugin-Dependencies,例如:

Manifest-Version: 1.0
Plugin-Dependencies: META-INF/lib/*
Plugin-Description: test
Plugin-BuildDate: 2017-11-08T12:12:27+0800
Archiver-Version: Plexus Archiver
SonarLint-Supported: true
Built-By: Administrator
Plugin-License: 
Plugin-Version: 0.0.1-SNAPSHOT
Sonar-Version: 5.6
Plugin-Developers: 
Plugin-ChildFirstClassLoader: false
Plugin-Key: javacustom
Plugin-Class: wxtx.com.MySonarPlugin
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_144
Plugin-Name: Java Custom Rules

并将依赖的jar包复制进META-INF/lib/目录下(例如guava、sonar-plugin-api)

5、测试

5.1、将自定义插件jar放到sonar下的extensions/plugins路径下
5.2、启动SonarQube
5.3、激活规则:登录admin账号后,点击activate按钮激活规则
SonarQube插件开发自定义规则(1)简易demo

6、参考

[1]sonar-custom-rules-examples