SonarQube, custom Java rules.
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Fri, 5 Aug 2016 00:46:06 +0000 (02:46 +0200)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Fri, 5 Aug 2016 00:46:06 +0000 (02:46 +0200)
28 files changed:
Sonar/Plugins/hello-world-plugin/pom.xml [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/CheckList.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.html [deleted file]
Sonar/Plugins/hello-world-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/HELLOWORLD0001_java.json [deleted file]
Sonar/Plugins/hello-world-plugin/src/test/files/HelloWorldCheck.java [deleted file]
Sonar/Plugins/hello-world-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java [deleted file]
Sonar/Plugins/pom.xml [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/.pom.xml.swp [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/pom.xml [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/CheckList.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/HelloWorldCheck.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheck.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldPlugin.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldProfile.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesCheckRegistrar.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldRulesDefinition.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/java/de/example/plugins/helloworld/HelloWorldSensor.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.html [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/main/resources/de/example/l10n/helloworld/rules/helloworld/GU0001_java.json [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/HelloWorldCheck.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/test/files/checks/SpringServiceInstanceFieldCheck.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/HelloWorldCheckTest.java [new file with mode: 0644]
Sonar/Plugins/sonar-custom-java-plugin/src/test/java/de/example/helloworld/checks/SpringServiceInstanceFieldCheckTest.java [new file with mode: 0644]

diff --git a/Sonar/Plugins/hello-world-plugin/pom.xml b/Sonar/Plugins/hello-world-plugin/pom.xml
deleted file mode 100644 (file)
index 0a00508..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<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>de.example.plugins</groupId>
-  <artifactId>helloworld-plugin</artifactId>
-  <packaging>sonar-plugin</packaging>
-  <version>0.1-SNAPSHOT</version>
-
-  <name>HelloWorld</name>
-  <description>HelloWorld example plugin for SonarQube</description>
-  <url>https://gumartinm.name/</url>
-  <organization>
-      <name>Gustavo Martin Morcuende</name>
-      <url>https://gumartinm.name/</url>
-  </organization>
-
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <sonar.apiVersion>5.6.1</sonar.apiVersion>
-    <java.plugin.version>4.0</java.plugin.version>
-    <jdk.min.version>1.8</jdk.min.version>
-    <sonar.pluginKey>helloworld</sonar.pluginKey>
-  </properties>
-
-  <dependencies>
-    <dependency>
-      <groupId>org.sonarsource.sonarqube</groupId>
-      <artifactId>sonar-plugin-api</artifactId>
-      <version>${sonar.apiVersion}</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>
-      <scope>provided</scope>
-    </dependency>
-      <dependency>
-        <groupId>com.google.code.gson</groupId>
-        <artifactId>gson</artifactId>
-        <version>2.6.2</version>
-        <scope>compile</scope>
-      </dependency>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-      <version>19.0</version>
-    </dependency>
-    <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-      <version>2.6</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>
-        </exclusions>
-      </dependency>
-          <dependency>
-      <groupId>org.sonarsource.java</groupId>
-      <artifactId>java-frontend</artifactId>
-      <version>4.0</version>
-    </dependency>
-      
-    
-
-
-    <!-- unit tests -->
-    <dependency>
-      <groupId>org.sonarsource.sonarqube</groupId>
-      <artifactId>sonar-testing-harness</artifactId>
-      <version>${sonar.apiVersion}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.sonarsource.java</groupId>
-      <artifactId>java-checks-testkit</artifactId>
-      <version>${java.plugin.version}</version>
-      <scope>test</scope>
-    </dependency>
-       <dependency>
-               <groupId>org.easytesting</groupId>
-               <artifactId>fest-assert</artifactId>
-               <version>1.4</version>
-               <scope>test</scope>
-       </dependency>
-       <dependency>
-               <groupId>junit</groupId>
-               <artifactId>junit</artifactId>
-               <version>4.11</version>
-               <scope>test</scope>
-       </dependency>
-    <!--
-       1/3 Required dependency for log4j 2 with slf4j: binding between log4j 
-       2 and slf4j
-    -->
-       <dependency>
-               <groupId>org.apache.logging.log4j</groupId>
-               <artifactId>log4j-slf4j-impl</artifactId>
-               <version>2.6.1</version>
-               <scope>test</scope>
-       </dependency>
-    <!--
-        2/3 Required dependency for log4j 2 with slf4j: log4j 2 maven plugin 
-        (it is the log4j 2 implementation)
-    -->
-       <dependency>
-               <groupId>org.apache.logging.log4j</groupId>
-               <artifactId>log4j-core</artifactId>
-               <version>2.6.1</version>
-               <scope>test</scope>
-       </dependency>
-    <!--
-        3/3 Required dependency for getting rid of commons logging. This is 
-        the BRIDGE (no binding) between Jakarta Commons Logging (used by Spring) 
-        and whatever I am using for logging (in this case I am using log4j 2) See: 
-        http://www.slf4j.org/legacy.html We need exclusions in every dependency using 
-        Jakarta Commons Logging (see Spring dependencies below) 
-    -->
-       <dependency>
-               <groupId>org.slf4j</groupId>
-               <artifactId>jcl-over-slf4j</artifactId>
-               <version>1.7.21</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>de.example.plugins.helloworld.HelloWorldPlugin</pluginClass>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.5.1</version>
-        <configuration>
-          <source>${jdk.min.version}</source>
-          <target>${jdk.min.version}</target>
-          <encoding>${project.build.sourceEncoding}</encoding>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
-
-</project>
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 (file)
index 226f174..0000000
+++ /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<Class> getChecks() {
-               return ImmutableList.<Class>builder()
-                               .addAll(getJavaChecks())
-                               .addAll(getJavaTestChecks())
-                               .addAll(getXmlChecks())
-                               .build();
-       }
-
-       public static List<Class<? extends JavaCheck>> getJavaChecks() {
-               return ImmutableList.<Class<? extends JavaCheck>>builder()
-                               .add(HelloWorldCheck.class)
-                               .build();
-       }
-
-       public static List<Class<? extends JavaCheck>> getJavaTestChecks() {
-               return ImmutableList.<Class<? extends JavaCheck>>builder()
-                               .build();
-       }
-
-       public static List<Class<? extends JavaCheck>> getXmlChecks() {
-               return ImmutableList.<Class<? extends JavaCheck>>builder()
-                               .build();
-       }
-
-       private static List<Class<? extends JavaCheck>> getMavenChecks() {
-               return ImmutableList.<Class<? extends JavaCheck>>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 (file)
index 3d4f2e2..0000000
+++ /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<Kind> 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 (file)
index 1c431ac..0000000
+++ /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<Object> 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 (file)
index c8e3700..0000000
+++ /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 (file)
index e88f017..0000000
+++ /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<Class> 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 (file)
index 4019e1b..0000000
+++ /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 (file)
index d0f8e3f..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<p>HelloWorld rule description.</p>
-<h2>Noncompliant Code Example</h2>
-<pre>
-class MyClass {
-       
-       int foo1(int value) { return 0; }
-               
-       MyClass foo2(MyClass value) { return null; }
-       
-       ...
-}
-</pre>
-<h2>See</h2>
-<ul>
-  <li> <a href="https://www.securecoding.cert.org/confluence/x/EYBUC">CERT, MSC11-J.</a> - Do not let session information leak within a servlet </li>
-</ul>
-
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 (file)
index 4421cd9..0000000
+++ /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 (file)
index 488a749..0000000
+++ /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 (file)
index d941f61..0000000
+++ /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 (file)
index 0000000..dd1430d
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>de.example.plugins</groupId>
+  <artifactId>custom-sonar-plugins</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <name>Custom SonarQube Plugins</name>
+  <description>Making custom SonarQube plugins</description>
+  <url>https://gumartinm.name</url>
+  <organization>
+      <name>Gustavo Martin Morcuende</name>
+      <url>https://gumartinm.name/</url>
+  </organization>
+
+    <scm>
+        <developerConnection>scm:git:https://git.gumartinm.name/JavaForFun</developerConnection>
+        <url>https://git.gumartinm.name/JavaForFun</url>
+    </scm>
+
+  <modules>
+    <module>sonar-custom-java-plugin</module>
+  </modules>
+
+
+</project>
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 (file)
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 (file)
index 0000000..8335378
--- /dev/null
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+
+    <parent>
+        <groupId>de.example.plugins</groupId>
+        <artifactId>custom-sonar-plugins</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+
+  <artifactId>sonar-custom-java-plugin</artifactId>
+  <packaging>sonar-plugin</packaging>
+
+  <name>SonarQube custom Java plugin</name>
+  <description>Java plugin for SonarQube</description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <sonar.apiVersion>5.6.1</sonar.apiVersion>
+    <java.plugin.version>4.0</java.plugin.version>
+    <jdk.min.version>1.8</jdk.min.version>
+    <sonar.pluginKey>helloworld</sonar.pluginKey>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.sonarsource.sonarqube</groupId>
+      <artifactId>sonar-plugin-api</artifactId>
+      <version>${sonar.apiVersion}</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>
+      <scope>provided</scope>
+    </dependency>
+      <dependency>
+        <groupId>com.google.code.gson</groupId>
+        <artifactId>gson</artifactId>
+        <version>2.6.2</version>
+        <scope>compile</scope>
+      </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>19.0</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.6</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>
+        </exclusions>
+      </dependency>
+          <dependency>
+      <groupId>org.sonarsource.java</groupId>
+      <artifactId>java-frontend</artifactId>
+      <version>4.0</version>
+    </dependency>
+      
+    
+
+
+    <!-- unit tests -->
+    <dependency>
+      <groupId>org.sonarsource.sonarqube</groupId>
+      <artifactId>sonar-testing-harness</artifactId>
+      <version>${sonar.apiVersion}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.sonarsource.java</groupId>
+      <artifactId>java-checks-testkit</artifactId>
+      <version>${java.plugin.version}</version>
+      <scope>test</scope>
+    </dependency>
+       <dependency>
+               <groupId>org.easytesting</groupId>
+               <artifactId>fest-assert</artifactId>
+               <version>1.4</version>
+               <scope>test</scope>
+       </dependency>
+       <dependency>
+               <groupId>junit</groupId>
+               <artifactId>junit</artifactId>
+               <version>4.11</version>
+               <scope>test</scope>
+       </dependency>
+    <!--
+       1/3 Required dependency for log4j 2 with slf4j: binding between log4j 
+       2 and slf4j
+    -->
+       <dependency>
+               <groupId>org.apache.logging.log4j</groupId>
+               <artifactId>log4j-slf4j-impl</artifactId>
+               <version>2.6.1</version>
+               <scope>test</scope>
+       </dependency>
+    <!--
+        2/3 Required dependency for log4j 2 with slf4j: log4j 2 maven plugin 
+        (it is the log4j 2 implementation)
+    -->
+       <dependency>
+               <groupId>org.apache.logging.log4j</groupId>
+               <artifactId>log4j-core</artifactId>
+               <version>2.6.1</version>
+               <scope>test</scope>
+       </dependency>
+    <!--
+        3/3 Required dependency for getting rid of commons logging. This is 
+        the BRIDGE (no binding) between Jakarta Commons Logging (used by Spring) 
+        and whatever I am using for logging (in this case I am using log4j 2) See: 
+        http://www.slf4j.org/legacy.html We need exclusions in every dependency using 
+        Jakarta Commons Logging (see Spring dependencies below) 
+    -->
+       <dependency>
+               <groupId>org.slf4j</groupId>
+               <artifactId>jcl-over-slf4j</artifactId>
+               <version>1.7.21</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>de.example.plugins.helloworld.HelloWorldPlugin</pluginClass>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.5.1</version>
+        <configuration>
+          <source>${jdk.min.version}</source>
+          <target>${jdk.min.version}</target>
+          <encoding>${project.build.sourceEncoding}</encoding>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
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 (file)
index 0000000..f9053a8
--- /dev/null
@@ -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<Class> getChecks() {
+               return ImmutableList.<Class>builder()
+                               .addAll(getJavaChecks())
+                               .addAll(getJavaTestChecks())
+                               .addAll(getXmlChecks())
+                               .build();
+       }
+
+       public static List<Class<? extends JavaCheck>> getJavaChecks() {
+               return ImmutableList.<Class<? extends JavaCheck>>builder()
+                               .add(HelloWorldCheck.class)
+                               .add(SpringServiceInstanceFieldCheck.class)
+                               .build();
+       }
+
+       public static List<Class<? extends JavaCheck>> getJavaTestChecks() {
+               return ImmutableList.<Class<? extends JavaCheck>>builder()
+                               .build();
+       }
+
+       public static List<Class<? extends JavaCheck>> getXmlChecks() {
+               return ImmutableList.<Class<? extends JavaCheck>>builder()
+                               .build();
+       }
+
+       private static List<Class<? extends JavaCheck>> getMavenChecks() {
+               return ImmutableList.<Class<? extends JavaCheck>>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 (file)
index 0000000..40eedf2
--- /dev/null
@@ -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<Kind> 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 (file)
index 0000000..db779fe
--- /dev/null
@@ -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<Kind> 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 (file)
index 0000000..1c431ac
--- /dev/null
@@ -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<Object> 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 (file)
index 0000000..ac02c42
--- /dev/null
@@ -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<String, String> 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<String, String> legacyKeys() {
+           Map<String, String> 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<String> 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 (file)
index 0000000..c8e3700
--- /dev/null
@@ -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 (file)
index 0000000..e88f017
--- /dev/null
@@ -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<Class> 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 (file)
index 0000000..4019e1b
--- /dev/null
@@ -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 (file)
index 0000000..d0f8e3f
--- /dev/null
@@ -0,0 +1,18 @@
+<p>HelloWorld rule description.</p>
+<h2>Noncompliant Code Example</h2>
+<pre>
+class MyClass {
+       
+       int foo1(int value) { return 0; }
+               
+       MyClass foo2(MyClass value) { return null; }
+       
+       ...
+}
+</pre>
+<h2>See</h2>
+<ul>
+  <li> <a href="https://www.securecoding.cert.org/confluence/x/EYBUC">CERT, MSC11-J.</a> - Do not let session information leak within a servlet </li>
+</ul>
+
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 (file)
index 0000000..4421cd9
--- /dev/null
@@ -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 (file)
index 0000000..488a749
--- /dev/null
@@ -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 (file)
index 0000000..16a7c80
--- /dev/null
@@ -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 (file)
index 0000000..34885a8
--- /dev/null
@@ -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 (file)
index 0000000..4b0e3a4
--- /dev/null
@@ -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());
+         }
+}