ClassCastException when using AssignmentExpressionTreeImpl

Template for a good bug report, formatted with Markdown:

  • versions used (SonarQube: 7.7.0.23042, Scanner : Jenkins Scanner 3.3.0.1492, Plugin, and any relevant extension)
    <sonar.version>6.7</sonar.version>
    <java.plugin.version>4.7.1.9272</java.plugin.version>
  • error observed
    private boolean hasSetFetchType(ExpressionTree et) {
        boolean result = false;
        System.out.println("KEN: class " + AssignmentExpressionTreeImpl.class);
        System.out.println("KEN: class " + et.getClass().getName().equals(AssignmentExpressionTreeImpl.class.getName()));
        if (et.getClass().getName().equals(AssignmentExpressionTreeImpl.class.getName())) {
            if ("fetch".equalsIgnoreCase(((AssignmentExpressionTreeImpl) et).variable().toString().trim())) {
                String fetchType = ((AssignmentExpressionTreeImpl) et).expression().lastToken().text();
                if ("LAZY".equalsIgnoreCase(fetchType.trim()) || "EAGER".equalsIgnoreCase(fetchType.trim())) {
                    result = true;
                }
                System.out.println("KEN: hasSetFetchType " + fetchType + ", result " + result);
            }
            System.out.println("KEN: hasSetFetchType " + ((AssignmentExpressionTreeImpl) et).variable().toString());
        }
        System.out.println("KEN: class " + et.getClass());
        return result;
    }
4:13:28.308 ERROR: Unable to run check class com.xuebangsoft.sonar.rule.check.RoRule - Ro on file /home/jenkins/workspace/Sonar-xb-biz-shell-uat-0513/biz-finance/src/main/java/com/eduboss/biz/shell/finance/studentAccMv/ro/ContractProductRo.java, To help improve SonarJava, please report this problem to SonarSource : see https://www.sonarqube.org/community/
java.lang.ClassCastException: org.sonar.java.model.expression.AssignmentExpressionTreeImpl cannot be cast to org.sonar.java.model.expression.AssignmentExpressionTreeImpl
	at com.xuebangsoft.sonar.rule.check.RoRule.hasSetFetchType(RoRule.java:161)
	at com.xuebangsoft.sonar.rule.check.RoRule.visitVariable(RoRule.java:88)
	at org.sonar.java.model.declaration.VariableTreeImpl.accept(VariableTreeImpl.java:185)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:34)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.visitClass(BaseTreeVisitor.java:68)
	at com.xuebangsoft.sonar.rule.check.RoRule.visitClass(RoRule.java:72)
	at org.sonar.java.model.declaration.ClassTreeImpl.accept(ClassTreeImpl.java:202)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:34)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.visitCompilationUnit(BaseTreeVisitor.java:52)
	at org.sonar.java.model.JavaTree$CompilationUnitTreeImpl.accept(JavaTree.java:188)
	at org.sonar.plugins.java.api.tree.BaseTreeVisitor.scan(BaseTreeVisitor.java:40)
	at com.xuebangsoft.sonar.rule.util.XBBaseTreeVisitor.scanFile(XBBaseTreeVisitor.java:37)
	at com.xuebangsoft.sonar.rule.check.RoRule.scanFile(RoRule.java:36)
	at org.sonar.java.model.VisitorsBridge.runScanner(VisitorsBridge.java:148)
	at org.sonar.java.model.VisitorsBridge.lambda$visitFile$1(VisitorsBridge.java:139)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.sonar.java.model.VisitorsBridge.visitFile(VisitorsBridge.java:139)
	at org.sonar.java.ast.JavaAstScanner.simpleScan(JavaAstScanner.java:96)
	at org.sonar.java.ast.JavaAstScanner.scan(JavaAstScanner.java:68)
	at org.sonar.java.JavaSquid.scanSources(JavaSquid.java:113)
	at org.sonar.java.JavaSquid.scan(JavaSquid.java:107)
	at org.sonar.plugins.java.JavaSquidSensor.execute(JavaSquidSensor.java:92)
	at org.sonar.scanner.sensor.AbstractSensorWrapper.analyse(AbstractSensorWrapper.java:48)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:85)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.lambda$execute$1(ModuleSensorsExecutor.java:59)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.withModuleStrategy(ModuleSensorsExecutor.java:77)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:59)
	at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:82)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
	at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:359)
	at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:354)
	at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:351)
	at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:317)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
	at org.sonar.scanner.bootstrap.GlobalContainer.doAfterStart(GlobalContainer.java:128)
	at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:136)
	at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:122)
	at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:73)
	at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:67)
	at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:46)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
	at com.sun.proxy.$Proxy0.execute(Unknown Source)
	at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:185)
	at org.sonarsource.scanner.api.EmbeddedScanner.execute(EmbeddedScanner.java:137)
	at org.sonarsource.scanner.cli.Main.execute(Main.java:111)
	at org.sonarsource.scanner.cli.Main.execute(Main.java:75)
	at org.sonarsource.scanner.cli.Main.main(Main.java:61)
  • steps to reproduce
    Put the customized java plugin together with the SonarJava plugin and the customized java plugin will face to the class loader issue.
    That is due to the SonarJava plugin is using its own classloader and event I set the attribute “true” in sonar-packaging-maven-plugin, it is still failed to load the class inside my customized plugin.
  • potential workaround

P.S.: use the #bug:fault sub-category if you’re hitting a specific crash/error , or the #bug:fp sub-category for rules-related behaviour

This is the workaround solution for me, do not use instanceof or convert to the AssignmentExpressionTreeImpl.class to avoid classloader issue


    private boolean hasSetFetchType(ExpressionTree et) {
        boolean result = false;
        if (et.getClass().getName().equals(AssignmentExpressionTreeImpl.class.getName())) {
            if ("fetch".equalsIgnoreCase(et.firstToken().text())) {
                String fetchType = et.lastToken().text();
                if ("LAZY".equalsIgnoreCase(fetchType.trim()) || "EAGER".equalsIgnoreCase(fetchType.trim())) {
                    result = true;
                }
            }
        }
        return result;
    }

Hello @rainyheart, and welcome on this forum.

When writting custom rules, you are never supposed to use any implementation classes from the SonarJava plugin. These classes are not exposed at runtime, and not available. See for instance this related thread:

Now, to get back to your rule. If you are trying to check if your ExpressionTree is an assignment, you should use the dedicated is(...) method of the Tree API, as introduced in the SonarJava tutorial for custom rules. You should rewrite your code to end up with something such as:

    private boolean hasSetFetchType(ExpressionTree et) {
        boolean result = false;
        if (et.is(Tree.Kind.ASSIGNMENT)) {
            // now you are sure that it's an assignment, you can cast directly to the interface
            AssignmentExpressionTree aet = (AssignmentExpressionTree) et;
            // searching on the variable side of the assignment
            if ("fetch".equalsIgnoreCase(aet.variable().firstToken().text())) {
                // searching on the expression side of theassignment
                String fetchType = aet.expression().lastToken().text();
                if ("LAZY".equalsIgnoreCase(fetchType.trim()) || "EAGER".equalsIgnoreCase(fetchType.trim())) {
                    result = true;
                }
            }
        }
        return result;
    }

Note that by relying only on first/last tokens, and therefore completely ignoring the AST structure, you will probably miss many uses cases, such as these ones, for instance:

fetch = ("LAZY"); 
fetch = "LAZY" + "";

I hope this helps.

Cheers,
Michael

1 Like

Hello Michael,

Thanks for your quick response.

But the issue is not related to how to use the API. According to the exception log, it’s due to the classloader of those instances in different plugins.

So in case there are two plugins which are using the same class for their rules implementations, there will be the same issue happened.




yejiazhi

邮箱:yjz_work@126.com

Signature is customized by Netease Mail Master

The exaception thrown to indicate that your code has attempted to cast an object to a subclass of which it is not an instance. This means that ClassCastException occurs when you try to cast an instance of an Object to a type that it is not. Type Casting 5 only works when the casted object follows an is a relationship to the type you are trying to cast to.

It is good practice to guard any explicit casts with an instanceof check first:

if (myApple instanceof Fruit) {
  Fruit myFruit = (Fruit)myApple;
}

When will be ClassCastException is thrown:

  • When you try to cast an object of Parent class to its Child class type, this exception will be thrown.

  • When you try to cast an object of one class into another class type that has not extended the other class or they don’t have any relationship between them