Search code examples
javasonarqubesonarqube-web

SonarQube custom plugin, failing on startup


I am developing a custom SonarQube plugin to create a project specific ruleset. Custom rule has been written and the rule is successfully executing using a JUnit. Below is the custom sonarqube rule.

@Rule(key = StringConstants.AVOID_SOCKETS_API_KEY)
public class AvoidSocketsApiRule extends IssuableSubscriptionVisitor {

    @Override
    public List<Tree.Kind> nodesToVisit() {
        return ImmutableList.of(Tree.Kind.VARIABLE);
    }

    @Override
    public void visitNode(final Tree tree) {
        final VariableTree variableTree = (VariableTree) tree;
        final Type type = variableTree.symbol().type();
        if (type.is(StringConstants.SOCKET_CHANNEL) || type.is(StringConstants.SERVER_SOCKET_CHANNEL)
                || type.is(StringConstants.ASYNC_SOCKET_CHANNEL) || type.is(StringConstants.ASYNC_SERVER_SOCKET_CHANNEL)) {
            this.reportIssue(variableTree.simpleName(), StringConstants.AVOID_SOCKETS_API_MESSAGE);
        }

    }

}

When I package the application into a sonar-plugin and deploy to sonarqube, its giving me a illegal state exception. I was able to replicate the issue using the Junit.

@Test
    public void test() {
        final CustomRulesDefinition rulesDefinition = new CustomRulesDefinition();
        final RulesDefinition.Context context = new RulesDefinition.Context();
        rulesDefinition.define(context);
        final RulesDefinition.Repository repository = context.repository(StringConstants.REPOSITORY_KEY);

        assertThat(repository.name()).isEqualTo(StringConstants.REPOSITORY_NAME);
        assertThat(repository.language()).isEqualTo(StringConstants.LANG);
        assertThat(repository.rules()).hasSize(RulesList.getChecks().size());

        this.assertAllRuleParametersHaveDescription(repository);
    }

    private void assertAllRuleParametersHaveDescription(final Repository repository) {
        for (final Rule rule : repository.rules()) {
            for (final Param param : rule.params()) {
                assertThat(param.description()).as("description for " + param.key()).isNotEmpty();
            }
        }
    }

The error displayed is provided below. I am unable to find a fix, appreciate your inputs here.

java.lang.IllegalStateException: Name of rule [repository=custom-ruleset, key=AvoidSocketsApi] is empty
    at org.sonar.api.server.rule.RulesDefinition$NewRule.validate(RulesDefinition.java:888)
    at org.sonar.api.server.rule.RulesDefinition$NewRule.access$800(RulesDefinition.java:659)
    at org.sonar.api.server.rule.RulesDefinition$RepositoryImpl.<init>(RulesDefinition.java:566)
    at org.sonar.api.server.rule.RulesDefinition$RepositoryImpl.<init>(RulesDefinition.java:540)
    at org.sonar.api.server.rule.RulesDefinition$Context.registerRepository(RulesDefinition.java:436)
    at org.sonar.api.server.rule.RulesDefinition$Context.access$500(RulesDefinition.java:380)
    at org.sonar.api.server.rule.RulesDefinition$NewRepositoryImpl.done(RulesDefinition.java:512)
    at com.deloitte.sonar.java.CustomRulesDefinition.define(CustomRulesDefinition.java:83)
    at com.deloitte.sonar.java.CustomRulesDefinitionTest.test(CustomRulesDefinitionTest.java:19)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Below is the pom.xml

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <sonar.plugin.api.version>5.6</sonar.plugin.api.version>
        <java.plugin.version>4.2</java.plugin.version>
        <junit.version>4.12</junit.version>
        <sslr.version>1.21</sslr.version>
        <gson.version>2.6.2</gson.version>
        <slf4j.version>1.6.2</slf4j.version>
        <logback.version>0.9.30</logback.version>
        <fest.assert.version>1.4</fest.assert.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.sonarsource.sonarqube</groupId>
            <artifactId>sonar-plugin-api</artifactId>
            <version>${sonar.plugin.api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.sonarsource.java</groupId>
            <artifactId>sonar-java-plugin</artifactId>
            <version>${java.plugin.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.sonarsource.java</groupId>
            <artifactId>java-frontend</artifactId>
            <version>${java.plugin.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.sonarsource.sslr-squid-bridge</groupId>
            <artifactId>sslr-squid-bridge</artifactId>
            <version>2.6.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.sonarsource.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>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>${gson.version}</version>
        </dependency>
        <dependency>
            <groupId>org.sonarsource.java</groupId>
            <artifactId>java-checks-testkit</artifactId>
            <version>${java.plugin.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.sonarsource.sslr</groupId>
            <artifactId>sslr-testing-harness</artifactId>
            <version>${sslr.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easytesting</groupId>
            <artifactId>fest-assert</artifactId>
            <version>${fest.assert.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</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>
                    <pluginClass>com.suraj.sonar.java.RulesPlugin</pluginClass>
                    <pluginDescription>Plugin for custom rules</pluginDescription>
                    <pluginOrganizationName>Suraj</pluginOrganizationName>
                    <pluginOrganizationUrl>surajmuraleedharanc.com</pluginOrganizationUrl>
                    <sonarLintSupported>true</sonarLintSupported>
                    <sonarQubeMinVersion>${sonar.plugin.api.version}</sonarQubeMinVersion>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

        </plugins>
    </build>

Solution

  • The crux of your question seems to be: Why does my rule work via unit tests but raise an exception via the SonarQube server?

    Your unit test simply executes some logic. It doesn't know or care about the concept of "rule".

    But the server deals not with logic, but with rules (which themselves contain logic). And by definition, a rule must have (among other things) a name. The custom rule quick start guide gives the details, but essentially, you need to add this:

    @Rule(
      key = "avoidSocketsApi",
      name = "The \"Sockets\" API should not be used",
      description = "...",
      priority = Priority.CRITICAL,
      tags = {"bug"})
    public class AvoidSocketsApiRule extends IssuableSubscriptionVisitor {