From: Gustavo Martin Morcuende Date: Fri, 5 Aug 2016 00:46:06 +0000 (+0200) Subject: SonarQube, custom Java rules. X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=bea1b4152f0d2fd10c270a9d3ed474c4968f928d;p=JavaForFun SonarQube, custom Java rules. --- diff --git a/Sonar/Plugins/hello-world-plugin/pom.xml b/Sonar/Plugins/hello-world-plugin/pom.xml deleted file mode 100644 index 0a00508..0000000 --- a/Sonar/Plugins/hello-world-plugin/pom.xml +++ /dev/null @@ -1,173 +0,0 @@ - - - 4.0.0 - - de.example.plugins - helloworld-plugin - sonar-plugin - 0.1-SNAPSHOT - - HelloWorld - HelloWorld example plugin for SonarQube - https://gumartinm.name/ - - Gustavo Martin Morcuende - https://gumartinm.name/ - - - - UTF-8 - 5.6.1 - 4.0 - 1.8 - helloworld - - - - - org.sonarsource.sonarqube - sonar-plugin-api - ${sonar.apiVersion} - provided - - - org.sonarsource.java - sonar-java-plugin - sonar-plugin - ${java.plugin.version} - provided - - - com.google.code.gson - gson - 2.6.2 - compile - - - com.google.guava - guava - 19.0 - - - commons-lang - commons-lang - 2.6 - - - org.sonarsource.sslr-squid-bridge - sslr-squid-bridge - 2.6.1 - - - org.codehaus.sonar.sslr - sslr-core - - - org.codehaus.sonar - sonar-plugin-api - - - org.codehaus.sonar.sslr - sslr-xpath - - - org.slf4j - jcl-over-slf4j - - - - - org.sonarsource.java - java-frontend - 4.0 - - - - - - - - org.sonarsource.sonarqube - sonar-testing-harness - ${sonar.apiVersion} - test - - - org.sonarsource.java - java-checks-testkit - ${java.plugin.version} - test - - - org.easytesting - fest-assert - 1.4 - test - - - junit - junit - 4.11 - test - - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.6.1 - test - - - - org.apache.logging.log4j - log4j-core - 2.6.1 - test - - - - org.slf4j - jcl-over-slf4j - 1.7.21 - test - - - - - - - - org.sonarsource.sonar-packaging-maven-plugin - sonar-packaging-maven-plugin - 1.17 - true - - de.example.plugins.helloworld.HelloWorldPlugin - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - ${jdk.min.version} - ${jdk.min.version} - ${project.build.sourceEncoding} - - - - - - diff --git a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/CheckList.java b/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/CheckList.java deleted file mode 100644 index 226f174..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/CheckList.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.example.helloworld.checks; - -import java.util.List; - -import org.sonar.plugins.java.api.JavaCheck; - -import com.google.common.collect.ImmutableList; - -public final class CheckList { - public static final String REPOSITORY_KEY = "helloworld"; - public static final String REPOSITORY_NAME = "Hello World"; - - private CheckList() { - } - - public static List getChecks() { - return ImmutableList.builder() - .addAll(getJavaChecks()) - .addAll(getJavaTestChecks()) - .addAll(getXmlChecks()) - .build(); - } - - public static List> getJavaChecks() { - return ImmutableList.>builder() - .add(HelloWorldCheck.class) - .build(); - } - - public static List> getJavaTestChecks() { - return ImmutableList.>builder() - .build(); - } - - public static List> getXmlChecks() { - return ImmutableList.>builder() - .build(); - } - - private static List> getMavenChecks() { - return ImmutableList.>builder() - .build(); - } -} diff --git a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java b/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java deleted file mode 100644 index 3d4f2e2..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.example.helloworld.checks; - -import java.util.List; - -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.check.Rule; -import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; -import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol; -import org.sonar.plugins.java.api.semantic.Type; -import org.sonar.plugins.java.api.tree.MethodTree; -import org.sonar.plugins.java.api.tree.Tree; -import org.sonar.plugins.java.api.tree.Tree.Kind; - -import com.google.common.collect.ImmutableList; - -@Rule(key = "HELLOWORLD0001") -public class HelloWorldCheck extends IssuableSubscriptionVisitor { - private static final Logger LOG = Loggers.get(HelloWorldCheck.class); - - - @Override - public List nodesToVisit() { - return ImmutableList.of(Kind.METHOD); - } - - @Override - public void visitNode(Tree tree) { - LOG.info("Visiting Node"); - - MethodTree method = (MethodTree) tree; - - if (method.parameters().size() == 1) { - MethodSymbol symbol = method.symbol(); - Type firstParameterType = symbol.parameterTypes().get(0); - Type returnType = symbol.returnType().type(); - if(returnType.is(firstParameterType.fullyQualifiedName())) { - reportIssue(method.simpleName(), "Never do that!"); - } - } - - } -} diff --git a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java b/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java deleted file mode 100644 index 1c431ac..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.example.plugins.helloworld; - -import org.sonar.api.Plugin; - -import com.google.common.collect.ImmutableList; - - -public class HelloWorldPlugin implements Plugin { - - @Override - public void define(Context context) { - - ImmutableList.Builder builder = ImmutableList.builder(); - builder.add( - HelloWorldRulesDefinition.class, - HelloWorldRulesCheckRegistrar.class); - - context.addExtensions(builder.build()); - } -} diff --git a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java b/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java deleted file mode 100644 index c8e3700..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.example.plugins.helloworld; - -import org.sonar.plugins.java.api.CheckRegistrar; - -import de.example.helloworld.checks.CheckList; - -public class HelloWorldRulesCheckRegistrar implements CheckRegistrar { - - @Override - public void register(RegistrarContext registrarContext) { - registrarContext.registerClassesForRepository(CheckList.REPOSITORY_KEY, CheckList.getJavaChecks(), CheckList.getJavaTestChecks()); - } - -} diff --git a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java b/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java deleted file mode 100644 index e88f017..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java +++ /dev/null @@ -1,141 +0,0 @@ -package de.example.plugins.helloworld; - -import java.io.IOException; -import java.net.URL; -import java.util.List; - -import javax.annotation.Nullable; - -import org.apache.commons.lang.StringUtils; -import org.sonar.api.rule.RuleStatus; -import org.sonar.api.server.debt.DebtRemediationFunction; -import org.sonar.api.server.rule.RulesDefinition; -import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader; -import org.sonar.api.utils.AnnotationUtils; -import org.sonar.check.Cardinality; -import org.sonar.plugins.java.Java; -import org.sonar.squidbridge.annotations.RuleTemplate; -import org.sonar.squidbridge.rules.ExternalDescriptionLoader; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.collect.Iterables; -import com.google.common.io.Resources; -import com.google.gson.Gson; - -import de.example.helloworld.checks.CheckList; - -/** - * Definition of rules. - */ -public class HelloWorldRulesDefinition implements RulesDefinition { - private static final String RESOURCE_BASE_PATH = "/de/example/l10n/helloworld/rules/helloworld"; - - private final Gson gson = new Gson(); - - @Override - public void define(Context context) { - NewRepository repository = context - .createRepository(CheckList.REPOSITORY_KEY, Java.KEY) - .setName(CheckList.REPOSITORY_NAME); - List checks = CheckList.getChecks(); - new RulesDefinitionAnnotationLoader().load(repository, Iterables.toArray(checks, Class.class)); - for (Class ruleClass : checks) { - newRule(ruleClass, repository); - } - repository.done(); - } - - @VisibleForTesting - protected void newRule(Class ruleClass, NewRepository repository) { - - org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(ruleClass, org.sonar.check.Rule.class); - if (ruleAnnotation == null) { - throw new IllegalArgumentException("No Rule annotation was found on " + ruleClass); - } - String ruleKey = ruleAnnotation.key(); - if (StringUtils.isEmpty(ruleKey)) { - throw new IllegalArgumentException("No key is defined in Rule annotation of " + ruleClass); - } - NewRule rule = repository.rule(ruleKey); - if (rule == null) { - throw new IllegalStateException("No rule was created for " + ruleClass + " in " + repository.key()); - } - rule.setTemplate(AnnotationUtils.getAnnotation(ruleClass, RuleTemplate.class) != null); - if (ruleAnnotation.cardinality() == Cardinality.MULTIPLE) { - throw new IllegalArgumentException("Cardinality is not supported, use the RuleTemplate annotation instead for " + ruleClass); - } - ruleMetadata(ruleClass, rule); - } - - private void ruleMetadata(Class ruleClass, NewRule rule) { - String metadataKey = rule.key(); - org.sonar.java.RspecKey rspecKeyAnnotation = AnnotationUtils.getAnnotation(ruleClass, org.sonar.java.RspecKey.class); - if (rspecKeyAnnotation != null) { - metadataKey = rspecKeyAnnotation.value(); - rule.setInternalKey(metadataKey); - } - addHtmlDescription(rule, metadataKey); - addMetadata(rule, metadataKey); - - } - - private void addMetadata(NewRule rule, String metadataKey) { - URL resource = ExternalDescriptionLoader.class.getResource(RESOURCE_BASE_PATH + "/" + metadataKey + "_java.json"); - if (resource != null) { - RuleMetatada metatada = gson.fromJson(readResource(resource), RuleMetatada.class); - rule.setSeverity(metatada.defaultSeverity.toUpperCase()); - rule.setName(metatada.title); - rule.addTags(metatada.tags); - rule.setStatus(RuleStatus.valueOf(metatada.status.toUpperCase())); - if(metatada.remediation != null) { - rule.setDebtRemediationFunction(metatada.remediation.remediationFunction(rule.debtRemediationFunctions())); - rule.setGapDescription(metatada.remediation.linearDesc); - } - } - } - - private static void addHtmlDescription(NewRule rule, String metadataKey) { - URL resource = HelloWorldRulesDefinition.class.getResource(RESOURCE_BASE_PATH + "/" + metadataKey + "_java.html"); - if (resource != null) { - rule.setHtmlDescription(readResource(resource)); - } - } - - private static String readResource(URL resource) { - try { - return Resources.toString(resource, Charsets.UTF_8); - } catch (IOException e) { - throw new IllegalStateException("Failed to read: " + resource, e); - } - } - - private static class RuleMetatada { - String title; - String status; - @Nullable - Remediation remediation; - - String[] tags; - String defaultSeverity; - } - - private static class Remediation { - String func; - String constantCost; - String linearDesc; - String linearOffset; - String linearFactor; - - public DebtRemediationFunction remediationFunction(DebtRemediationFunctions drf) { - if(func.startsWith("Constant")) { - return drf.constantPerIssue(constantCost.replace("mn", "min")); - } - if("Linear".equals(func)) { - return drf.linear(linearFactor.replace("mn", "min")); - } - return drf.linearWithOffset(linearFactor.replace("mn", "min"), linearOffset.replace("mn", "min")); - } - } - -} diff --git a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java b/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java deleted file mode 100644 index 4019e1b..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.example.plugins.helloworld; - -import org.sonar.api.batch.sensor.Sensor; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.plugins.java.Java; - -public class HelloWorldSensor implements Sensor { - - @Override - public void describe(SensorDescriptor descriptor) { - descriptor.onlyOnLanguage(Java.KEY); - descriptor.name("HelloWorld Sensor"); - } - - @Override - public void execute(SensorContext context) { - - } - -} diff --git a/Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.html b/Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.html deleted file mode 100644 index d0f8e3f..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.html +++ /dev/null @@ -1,18 +0,0 @@ -

HelloWorld rule description.

-

Noncompliant Code Example

-
-class MyClass {
-	
-	int foo1(int value) { return 0; }
-		
-	MyClass foo2(MyClass value) { return null; }
-	
-	...
- 
-}
-
-

See

-
    -
  • CERT, MSC11-J. - Do not let session information leak within a servlet
  • -
- diff --git a/Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.json b/Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.json deleted file mode 100644 index 4421cd9..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "title": "HelloWorld SonarQube rule", - "status": "ready", - "remediation": { - "func": "Constant\/Issue", - "constantCost": "5min" - }, - "tags": [ - "bad-practice" - ], - "defaultSeverity": "Major" -} diff --git a/Sonar/Plugins/hello-world-plugin/src/test/files/HelloWorldCheck.java b/Sonar/Plugins/hello-world-plugin/src/test/files/HelloWorldCheck.java deleted file mode 100644 index 488a749..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/test/files/HelloWorldCheck.java +++ /dev/null @@ -1,17 +0,0 @@ -class MyClass { - - MyClass(MyClass mc) { } - - int foo1() { return 0; } - - void foo2(int value) { } - - // Noncompliant@+1 [[startColumn=6;endLine=+0;endColumn=10;effortToFix=4]] {{Never do that!}} - int foo3(int value) { return 0; } - - Object foo4(int value) { return null; } - - // Noncompliant@+1 [[startColumn=10;endLine=+0;endColumn=14;effortToFix=4]] {{Never do that!}} - MyClass foo5(MyClass value) { return null; } - -} diff --git a/Sonar/Plugins/hello-world-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java b/Sonar/Plugins/hello-world-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java deleted file mode 100644 index d941f61..0000000 --- a/Sonar/Plugins/hello-world-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.example.helloworld.checks; - -import org.junit.Test; -import org.sonar.java.checks.verifier.JavaCheckVerifier; - -public class HelloWorldCheckTest { - - @Test - public void test() { - JavaCheckVerifier.verify("src/test/files/HelloWorldCheck.java", new HelloWorldCheck()); - } -} diff --git a/Sonar/Plugins/pom.xml b/Sonar/Plugins/pom.xml new file mode 100644 index 0000000..dd1430d --- /dev/null +++ b/Sonar/Plugins/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + de.example.plugins + custom-sonar-plugins + 0.1-SNAPSHOT + pom + Custom SonarQube Plugins + Making custom SonarQube plugins + https://gumartinm.name + + Gustavo Martin Morcuende + https://gumartinm.name/ + + + + scm:git:https://git.gumartinm.name/JavaForFun + https://git.gumartinm.name/JavaForFun + + + + sonar-custom-java-plugin + + + + diff --git a/Sonar/Plugins/sonar-custom-java-plugin/.pom.xml.swp b/Sonar/Plugins/sonar-custom-java-plugin/.pom.xml.swp new file mode 100644 index 0000000..4c402d5 Binary files /dev/null and b/Sonar/Plugins/sonar-custom-java-plugin/.pom.xml.swp differ diff --git a/Sonar/Plugins/sonar-custom-java-plugin/pom.xml b/Sonar/Plugins/sonar-custom-java-plugin/pom.xml new file mode 100644 index 0000000..8335378 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/pom.xml @@ -0,0 +1,172 @@ + + + 4.0.0 + + + de.example.plugins + custom-sonar-plugins + 0.1-SNAPSHOT + + + sonar-custom-java-plugin + sonar-plugin + + SonarQube custom Java plugin + Java plugin for SonarQube + + + UTF-8 + 5.6.1 + 4.0 + 1.8 + helloworld + + + + + org.sonarsource.sonarqube + sonar-plugin-api + ${sonar.apiVersion} + provided + + + org.sonarsource.java + sonar-java-plugin + sonar-plugin + ${java.plugin.version} + provided + + + com.google.code.gson + gson + 2.6.2 + compile + + + com.google.guava + guava + 19.0 + + + commons-lang + commons-lang + 2.6 + + + org.sonarsource.sslr-squid-bridge + sslr-squid-bridge + 2.6.1 + + + org.codehaus.sonar.sslr + sslr-core + + + org.codehaus.sonar + sonar-plugin-api + + + org.codehaus.sonar.sslr + sslr-xpath + + + org.slf4j + jcl-over-slf4j + + + + + org.sonarsource.java + java-frontend + 4.0 + + + + + + + + org.sonarsource.sonarqube + sonar-testing-harness + ${sonar.apiVersion} + test + + + org.sonarsource.java + java-checks-testkit + ${java.plugin.version} + test + + + org.easytesting + fest-assert + 1.4 + test + + + junit + junit + 4.11 + test + + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.6.1 + test + + + + org.apache.logging.log4j + log4j-core + 2.6.1 + test + + + + org.slf4j + jcl-over-slf4j + 1.7.21 + test + + + + + + + + org.sonarsource.sonar-packaging-maven-plugin + sonar-packaging-maven-plugin + 1.17 + true + + de.example.plugins.helloworld.HelloWorldPlugin + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + ${jdk.min.version} + ${jdk.min.version} + ${project.build.sourceEncoding} + + + + + + diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/CheckList.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/CheckList.java new file mode 100644 index 0000000..f9053a8 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/CheckList.java @@ -0,0 +1,45 @@ +package de.example.helloworld.checks; + +import java.util.List; + +import org.sonar.plugins.java.api.JavaCheck; + +import com.google.common.collect.ImmutableList; + +public final class CheckList { + public static final String REPOSITORY_KEY = "helloworld"; + public static final String REPOSITORY_NAME = "Hello World"; + + private CheckList() { + } + + public static List getChecks() { + return ImmutableList.builder() + .addAll(getJavaChecks()) + .addAll(getJavaTestChecks()) + .addAll(getXmlChecks()) + .build(); + } + + public static List> getJavaChecks() { + return ImmutableList.>builder() + .add(HelloWorldCheck.class) + .add(SpringServiceInstanceFieldCheck.class) + .build(); + } + + public static List> getJavaTestChecks() { + return ImmutableList.>builder() + .build(); + } + + public static List> getXmlChecks() { + return ImmutableList.>builder() + .build(); + } + + private static List> getMavenChecks() { + return ImmutableList.>builder() + .build(); + } +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java new file mode 100644 index 0000000..40eedf2 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java @@ -0,0 +1,43 @@ +package de.example.helloworld.checks; + +import java.util.List; + +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol; +import org.sonar.plugins.java.api.semantic.Type; +import org.sonar.plugins.java.api.tree.MethodTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.Tree.Kind; + +import com.google.common.collect.ImmutableList; + +@Rule(key = "GU0001") +public class HelloWorldCheck extends IssuableSubscriptionVisitor { + private static final Logger LOG = Loggers.get(HelloWorldCheck.class); + + + @Override + public List nodesToVisit() { + return ImmutableList.of(Kind.METHOD); + } + + @Override + public void visitNode(Tree tree) { + LOG.info("Visiting Node"); + + MethodTree method = (MethodTree) tree; + + if (method.parameters().size() == 1) { + MethodSymbol symbol = method.symbol(); + Type firstParameterType = symbol.parameterTypes().get(0); + Type returnType = symbol.returnType().type(); + if(returnType.is(firstParameterType.fullyQualifiedName())) { + reportIssue(method.simpleName(), "Never do that!"); + } + } + + } +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheck.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheck.java new file mode 100644 index 0000000..db779fe --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheck.java @@ -0,0 +1,51 @@ +package de.example.helloworld.checks; + +import java.util.List; + +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.tree.ClassTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.Tree.Kind; +import org.sonar.plugins.java.api.tree.VariableTree; + +import com.google.common.collect.ImmutableList; + +@Rule(key = "GU0002") +public class SpringServiceInstanceFieldCheck extends IssuableSubscriptionVisitor { + private static final Logger LOG = Loggers.get(SpringServiceInstanceFieldCheck.class); + + private JavaFileScannerContext context; + + + @Override + public List nodesToVisit() { + return ImmutableList.of(Kind.CLASS, Kind.VARIABLE); + } + + @Override + public void visitNode(Tree tree) { + if (tree.is(Kind.CLASS) && isSpringService((ClassTree) tree)) { + + } + + } + + + private static boolean isOwnedByASpringService(VariableTree variable) { + Symbol owner = variable.symbol().owner(); + return owner.isTypeSymbol() && (owner.type().isSubtypeOf("javax.servlet.http.HttpServlet") || owner.type().isSubtypeOf("org.apache.struts.action.Action")); + } + + private static boolean isSpringService(ClassTree tree) { + tree.symbol().metadata().isAnnotatedWith("javax.inject.Inject"); + return true; + + } + + +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java new file mode 100644 index 0000000..1c431ac --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java @@ -0,0 +1,20 @@ +package de.example.plugins.helloworld; + +import org.sonar.api.Plugin; + +import com.google.common.collect.ImmutableList; + + +public class HelloWorldPlugin implements Plugin { + + @Override + public void define(Context context) { + + ImmutableList.Builder builder = ImmutableList.builder(); + builder.add( + HelloWorldRulesDefinition.class, + HelloWorldRulesCheckRegistrar.class); + + context.addExtensions(builder.build()); + } +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldProfile.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldProfile.java new file mode 100644 index 0000000..ac02c42 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldProfile.java @@ -0,0 +1,70 @@ +package de.example.plugins.helloworld; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonar.api.profiles.ProfileDefinition; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.utils.AnnotationUtils; +import org.sonar.api.utils.ValidationMessages; +import org.sonar.java.checks.CheckList; +import org.sonar.plugins.java.Java; +import org.sonar.plugins.java.JavaRulesDefinition; + +import com.google.common.base.Charsets; +import com.google.common.io.Resources; +import com.google.gson.Gson; + +public class HelloWorldProfile extends ProfileDefinition { + + private final Gson gson = new Gson(); + private final RuleFinder ruleFinder; + public HelloWorldProfile(RuleFinder ruleFinder) { + this.ruleFinder = ruleFinder; + } + + @Override + public RulesProfile createProfile(ValidationMessages messages) { + RulesProfile profile = RulesProfile.create("Sonar way", Java.KEY); + URL resource = JavaRulesDefinition.class.getResource("/org/sonar/l10n/java/rules/squid/Sonar_way_profile.json"); + Profile jsonProfile = gson.fromJson(readResource(resource), Profile.class); + Map keys = legacyKeys(); + for (String key : jsonProfile.ruleKeys) { + profile.activateRule(ruleFinder.findByKey(CheckList.REPOSITORY_KEY, keys.get(key)), null); + } + return profile; + } + + private static String readResource(URL resource) { + try { + return Resources.toString(resource, Charsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException("Failed to read: " + resource, e); + } + } + + private static Map legacyKeys() { + Map result = new HashMap<>(); + for (Class checkClass : CheckList.getChecks()) { + org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(checkClass, org.sonar.check.Rule.class); + String key = ruleAnnotation.key(); + org.sonar.java.RspecKey rspecKeyAnnotation = AnnotationUtils.getAnnotation(checkClass, org.sonar.java.RspecKey.class); + String rspecKey = key; + if(rspecKeyAnnotation != null) { + rspecKey = rspecKeyAnnotation.value(); + } + result.put(rspecKey, key); + } + return result; + } + + private static class Profile { + String name; + List ruleKeys; + } + + } diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java new file mode 100644 index 0000000..c8e3700 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java @@ -0,0 +1,14 @@ +package de.example.plugins.helloworld; + +import org.sonar.plugins.java.api.CheckRegistrar; + +import de.example.helloworld.checks.CheckList; + +public class HelloWorldRulesCheckRegistrar implements CheckRegistrar { + + @Override + public void register(RegistrarContext registrarContext) { + registrarContext.registerClassesForRepository(CheckList.REPOSITORY_KEY, CheckList.getJavaChecks(), CheckList.getJavaTestChecks()); + } + +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java new file mode 100644 index 0000000..e88f017 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java @@ -0,0 +1,141 @@ +package de.example.plugins.helloworld; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +import javax.annotation.Nullable; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.rule.RuleStatus; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader; +import org.sonar.api.utils.AnnotationUtils; +import org.sonar.check.Cardinality; +import org.sonar.plugins.java.Java; +import org.sonar.squidbridge.annotations.RuleTemplate; +import org.sonar.squidbridge.rules.ExternalDescriptionLoader; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.collect.Iterables; +import com.google.common.io.Resources; +import com.google.gson.Gson; + +import de.example.helloworld.checks.CheckList; + +/** + * Definition of rules. + */ +public class HelloWorldRulesDefinition implements RulesDefinition { + private static final String RESOURCE_BASE_PATH = "/de/example/l10n/helloworld/rules/helloworld"; + + private final Gson gson = new Gson(); + + @Override + public void define(Context context) { + NewRepository repository = context + .createRepository(CheckList.REPOSITORY_KEY, Java.KEY) + .setName(CheckList.REPOSITORY_NAME); + List checks = CheckList.getChecks(); + new RulesDefinitionAnnotationLoader().load(repository, Iterables.toArray(checks, Class.class)); + for (Class ruleClass : checks) { + newRule(ruleClass, repository); + } + repository.done(); + } + + @VisibleForTesting + protected void newRule(Class ruleClass, NewRepository repository) { + + org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(ruleClass, org.sonar.check.Rule.class); + if (ruleAnnotation == null) { + throw new IllegalArgumentException("No Rule annotation was found on " + ruleClass); + } + String ruleKey = ruleAnnotation.key(); + if (StringUtils.isEmpty(ruleKey)) { + throw new IllegalArgumentException("No key is defined in Rule annotation of " + ruleClass); + } + NewRule rule = repository.rule(ruleKey); + if (rule == null) { + throw new IllegalStateException("No rule was created for " + ruleClass + " in " + repository.key()); + } + rule.setTemplate(AnnotationUtils.getAnnotation(ruleClass, RuleTemplate.class) != null); + if (ruleAnnotation.cardinality() == Cardinality.MULTIPLE) { + throw new IllegalArgumentException("Cardinality is not supported, use the RuleTemplate annotation instead for " + ruleClass); + } + ruleMetadata(ruleClass, rule); + } + + private void ruleMetadata(Class ruleClass, NewRule rule) { + String metadataKey = rule.key(); + org.sonar.java.RspecKey rspecKeyAnnotation = AnnotationUtils.getAnnotation(ruleClass, org.sonar.java.RspecKey.class); + if (rspecKeyAnnotation != null) { + metadataKey = rspecKeyAnnotation.value(); + rule.setInternalKey(metadataKey); + } + addHtmlDescription(rule, metadataKey); + addMetadata(rule, metadataKey); + + } + + private void addMetadata(NewRule rule, String metadataKey) { + URL resource = ExternalDescriptionLoader.class.getResource(RESOURCE_BASE_PATH + "/" + metadataKey + "_java.json"); + if (resource != null) { + RuleMetatada metatada = gson.fromJson(readResource(resource), RuleMetatada.class); + rule.setSeverity(metatada.defaultSeverity.toUpperCase()); + rule.setName(metatada.title); + rule.addTags(metatada.tags); + rule.setStatus(RuleStatus.valueOf(metatada.status.toUpperCase())); + if(metatada.remediation != null) { + rule.setDebtRemediationFunction(metatada.remediation.remediationFunction(rule.debtRemediationFunctions())); + rule.setGapDescription(metatada.remediation.linearDesc); + } + } + } + + private static void addHtmlDescription(NewRule rule, String metadataKey) { + URL resource = HelloWorldRulesDefinition.class.getResource(RESOURCE_BASE_PATH + "/" + metadataKey + "_java.html"); + if (resource != null) { + rule.setHtmlDescription(readResource(resource)); + } + } + + private static String readResource(URL resource) { + try { + return Resources.toString(resource, Charsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException("Failed to read: " + resource, e); + } + } + + private static class RuleMetatada { + String title; + String status; + @Nullable + Remediation remediation; + + String[] tags; + String defaultSeverity; + } + + private static class Remediation { + String func; + String constantCost; + String linearDesc; + String linearOffset; + String linearFactor; + + public DebtRemediationFunction remediationFunction(DebtRemediationFunctions drf) { + if(func.startsWith("Constant")) { + return drf.constantPerIssue(constantCost.replace("mn", "min")); + } + if("Linear".equals(func)) { + return drf.linear(linearFactor.replace("mn", "min")); + } + return drf.linearWithOffset(linearFactor.replace("mn", "min"), linearOffset.replace("mn", "min")); + } + } + +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java new file mode 100644 index 0000000..4019e1b --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java @@ -0,0 +1,21 @@ +package de.example.plugins.helloworld; + +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.plugins.java.Java; + +public class HelloWorldSensor implements Sensor { + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.onlyOnLanguage(Java.KEY); + descriptor.name("HelloWorld Sensor"); + } + + @Override + public void execute(SensorContext context) { + + } + +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.html b/Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.html new file mode 100644 index 0000000..d0f8e3f --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.html @@ -0,0 +1,18 @@ +

HelloWorld rule description.

+

Noncompliant Code Example

+
+class MyClass {
+	
+	int foo1(int value) { return 0; }
+		
+	MyClass foo2(MyClass value) { return null; }
+	
+	...
+ 
+}
+
+

See

+
    +
  • CERT, MSC11-J. - Do not let session information leak within a servlet
  • +
+ diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.json b/Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.json new file mode 100644 index 0000000..4421cd9 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.json @@ -0,0 +1,12 @@ +{ + "title": "HelloWorld SonarQube rule", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "bad-practice" + ], + "defaultSeverity": "Major" +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/HelloWorldCheck.java b/Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/HelloWorldCheck.java new file mode 100644 index 0000000..488a749 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/HelloWorldCheck.java @@ -0,0 +1,17 @@ +class MyClass { + + MyClass(MyClass mc) { } + + int foo1() { return 0; } + + void foo2(int value) { } + + // Noncompliant@+1 [[startColumn=6;endLine=+0;endColumn=10;effortToFix=4]] {{Never do that!}} + int foo3(int value) { return 0; } + + Object foo4(int value) { return null; } + + // Noncompliant@+1 [[startColumn=10;endLine=+0;endColumn=14;effortToFix=4]] {{Never do that!}} + MyClass foo5(MyClass value) { return null; } + +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/SpringServiceInstanceFieldCheck.java b/Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/SpringServiceInstanceFieldCheck.java new file mode 100644 index 0000000..16a7c80 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/SpringServiceInstanceFieldCheck.java @@ -0,0 +1,18 @@ +import javax.inject.Named; + +@Named("aService") +public class AService { + private static final Integer FIELD1; + + private final Integer field2; + + private Integer field3; + + public static final Integer field4; + + public final Integer field5; + + // Noncompliant@+1 [[startColumn=6;endLine=+0;endColumn=10;effortToFix=4]] {{Remove this misleading mutable service instance fields or make it \"static\" and/or \"final\"}} + public Integer field6; + +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java b/Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java new file mode 100644 index 0000000..34885a8 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java @@ -0,0 +1,12 @@ +package de.example.helloworld.checks; + +import org.junit.Test; +import org.sonar.java.checks.verifier.JavaCheckVerifier; + +public class HelloWorldCheckTest { + + @Test + public void test() { + JavaCheckVerifier.verify("src/test/files/checks/HelloWorldCheck.java", new HelloWorldCheck()); + } +} diff --git a/Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheckTest.java b/Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheckTest.java new file mode 100644 index 0000000..4b0e3a4 --- /dev/null +++ b/Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheckTest.java @@ -0,0 +1,13 @@ +package de.example.helloworld.checks; + +import org.junit.Test; +import org.sonar.java.checks.verifier.JavaCheckVerifier; + +public class SpringServiceInstanceFieldCheckTest { + private static final String FILENAME = "src/test/files/checks/SpringServiceInstanceFieldCheck.java"; + + @Test + public void test() { + JavaCheckVerifier.verify(FILENAME, new SpringServiceInstanceFieldCheck()); + } +}