Using org.sonar.java.matcher.MethodMatcher package in a custom rule

SONARQUBE version 7.7
Hi, i need to write a rule that check if exist a class that extends “myclass”.
If exist it is mandatory that this class overrides “mymethod”.
I’m used for write this rule, the sonar standard class for the rule S2160 that use org.sonar.java.matcher.MethodMatcher package.
below my code:

package org.sonar.samples.java.checks;
import org.sonar.check.Rule;
import org.sonar.java.matcher.MethodMatcher;
import org.sonar.java.matcher.TypeCriteria;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.Type;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.NewClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import java.util.Collections;
import java.util.List;
import org.sonar.check.Priority;
 
@Rule(
		  key = "MySecondCustomCheck",
		  name = "MyClass class needs override MyMethod",
		  description = "MyClass class needs override MyMethod",
		  priority = Priority.CRITICAL,
		  tags = {"bug"})

public class MySecondCustomCheck extends IssuableSubscriptionVisitor {
	  private static final String MY_CLASS = "MyClass";
	  private static final MethodMatcher MyMethod = MethodMatcher.create().typeDefinition(TypeCriteria.subtypeOf(MY_CLASS)).name("MyMethod");
  @Override
  public List<Tree.Kind> nodesToVisit() {
	    return Collections.singletonList(Tree.Kind.CLASS);
	  }	 
  
	  @Override
	  public void visitNode(Tree tree) {
	    ClassTree classTree = (ClassTree) tree;
	    Symbol.TypeSymbol classSymbol = classTree.symbol();
	    if (classSymbol != null
	      && isDirectSubtypeOfThread(classSymbol)
	      && !overridesMyMethodMethod(classSymbol)){
	      Tree report = classTree.simpleName();
	      Tree parent = classTree.parent();
	      if(parent.is(Tree.Kind.NEW_CLASS)) {
	        NewClassTree newClassTree = (NewClassTree) parent;
	        report = newClassTree.identifier();
	      }
	      reportIssue(report, "Don't extend \"MY_CLASS\", since the \"MyMethod\" method is not overridden.");
	    }
	  }

	  private static boolean isDirectSubtypeOfThread(Symbol.TypeSymbol classSymbol) {
		    Type superClass = classSymbol.superClass();
		    return superClass != null && superClass.is(MY_CLASS);
		  }

		  private static boolean overridesMyMethodMethod(Symbol.TypeSymbol classSymbol) {
		    return classSymbol.lookupSymbols("MyMethod").stream().anyMatch(MyMethod::matches);
		  }
}

I have this error when i try to restart SonarQube:

2019.06.04 12:06:25 ERROR web[][o.s.s.p.Platform] Background initialization failed. Stopping SonarQube
java.lang.NoClassDefFoundError: Lorg/sonar/java/matcher/MethodMatcher;
	at java.lang.Class.getDeclaredFields0(Native Method)
	at java.lang.Class.privateGetDeclaredFields(Unknown Source)
	at java.lang.Class.getDeclaredFields(Unknown Source)
	at org.sonar.api.utils.FieldUtils2.getFields(FieldUtils2.java:51)
	at org.sonar.api.server.rule.RulesDefinitionAnnotationLoader.loadRule(RulesDefinitionAnnotationLoader.java:89)
	at org.sonar.api.server.rule.RulesDefinitionAnnotationLoader.loadRule(RulesDefinitionAnnotationLoader.java:70)
	at org.sonar.api.server.rule.RulesDefinitionAnnotationLoader.load(RulesDefinitionAnnotationLoader.java:62)
	at org.sonar.samples.java.MyJavaRulesDefinition.define(MyJavaRulesDefinition.java:59)
	at org.sonar.server.rule.RuleDefinitionsLoader.load(RuleDefinitionsLoad

Any Help?

The class org.sonar.java.matcher.MethodMatcher is not from api package. It is in java-frontend module which is marked as provided during compile time. But only classes under api packages are provided during analyze time or during rules loading at the server side. So you have to include and shadow the java-frontend package or copy the class to your plugin.

2 Likes

Thank you! it work!

Hey @bruna and @Josef_Prochazka (thanks for your answer!)

As a complementary note, I would like to add that we are currently planning to add the MethodMatchers to the SonarJava API. I would consequently strongly encourage you to vote for the following ticket in our backlog: SONARJAVA-2357

Cheers,
Michael

As I undestand,

I should add the dependency to java-frontend to my pom.xml:

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

To shade the dependency I’m trying to use the plugin maven-shade-plugin, so, I added to the plugins section of my pom.xml:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.2.0</version>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<artifactSet>
					<excludes>
						<exclude>org.sonarsource.java:java-frontend</exclude>
					</excludes>
				</artifactSet>
			</configuration>
		</execution>
	</executions>
</plugin>

SonarQube server starts succesfully. But, when I run an analysis that uses the new rule I obtain the following error (in the computer that is performing mvn sonar:sonar):

[ERROR] Failed to execute goal 
org.sonarsource.scanner.maven:sonar-maven-plugin:3.6.0.1398:sonar (default-cli) 
on project xxx: SonarQube is unable to analyze file : 'xxx/src/main/java/com/ClassFile.java': 
class org.sonar.java.resolve.JavaSymbol$MethodJavaSymbol cannot be cast to
class org.sonar.java.resolve.JavaSymbol$MethodJavaSymbol
(org.sonar.java.resolve.JavaSymbol$MethodJavaSymbol is in unnamed module of 
loader org.sonar.classloader.ClassRealm @6f66ab8f;

What I’m doing wrong?
Any help?

I am not sure at the moment but I think it should be like this.

<configuration>
<relocations>
<relocation>
<pattern>org.sonar.java.matcher</patern>
</relocation>
</relocations>
</configuration>
1 Like

Thank you!
It works perfectly!