After scanning with custom rules in version 9.9, java.lang.NoClassDefFoundError: com/sun/source/tree

What are you trying to accomplish?
Scan for normal execution

  • Support a new language?
    No
  • Extend an existing one? Which one?
    java
  • Add language-agnostic features?
    No
  • Something else?
    Add formatting code checking

What’s your specific coding challenge in developing your plugin?
I use googlejavaformat to format the code. When I deploy the plugin, I run a scan and an error occurs, prompting java.lang.NoClassDefFoundError: com/sun/source/tree/Tree
I build the jar package in jdk17.
My sonarqube is deployed using docker.

And, if relevant, please share the code that’s giving you problems:

@Rule(key = JavaCodeFormatRule.RULE_KEY, name = "code format check", description = "XXXXXXX")
public class JavaCodeFormatRule implements IRule {

    private static final Logger LOGGER = Loggers.get(JavaCodeFormatRule.class);

    public static final String RULE_KEY = "JavaCodeFormatRule";

    @Override
    public void execute(SensorContext sensorContext, InputFile file, RuleKey ruleKey) {
        try {
            String contents = file.contents();
            Formatter formatter = new Formatter(JavaFormatterOptions.builder()
                    .style(JavaFormatterOptions.Style.AOSP)
                    .formatJavadoc(true)
                    .reorderModifiers(false)
                    .build());
            String formattedCode = formatter.formatSource(contents);

            if (!Objects.equals(contents, formattedCode)) {
                LOGGER.error("代码未格式化!");
                NewIssue newIssue = sensorContext.newIssue();
                newIssue.forRule(ruleKey)
                        .at(newIssue.newLocation().on(file).at(file.selectLine(1)))
                        .save();
            }
        } catch (IOException | FormatterException e) {
            throw new RuntimeException(e);
        }
    }
}

The specific error is

10:31:06.548 INFO  Execute PMD 6.15.0 (done) | time=108493ms
10:31:06.652 INFO  Sensor PmdSensor [pmd] (done) | time=108598ms
10:31:06.652 INFO  Sensor JaCoCo XML Report Importer [jacoco]
10:31:06.654 INFO  'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml
10:31:06.655 INFO  No report imported, no coverage information will be imported by JaCoCo XML Report Importer
10:31:06.655 INFO  Sensor JaCoCo XML Report Importer [jacoco] (done) | time=3ms
10:31:06.655 INFO  Sensor IaC CloudFormation Sensor [iac]
10:31:06.678 INFO  0 source files to be analyzed
10:31:06.690 INFO  0/0 source files have been analyzed
10:31:06.690 INFO  Sensor IaC CloudFormation Sensor [iac] (done) | time=35ms
10:31:06.690 INFO  Sensor IaC Kubernetes Sensor [iac]
10:31:06.703 INFO  0 source files to be analyzed
10:31:06.704 INFO  0/0 source files have been analyzed
10:31:06.705 INFO  Sensor IaC Kubernetes Sensor [iac] (done) | time=15ms
10:31:06.705 INFO  Sensor JavaScript inside YAML analysis [javascript]
10:31:06.710 INFO  No input files found for analysis
10:31:06.711 INFO  Hit the cache for 0 out of 0
10:31:06.713 INFO  Miss the cache for 0 out of 0
10:31:06.713 INFO  Sensor JavaScript inside YAML analysis [javascript] (done) | time=8ms
10:31:06.713 INFO  Sensor CSS Rules [javascript]
10:31:06.719 INFO  No CSS, PHP, HTML or VueJS files are found in the project. CSS analysis is skipped.
10:31:06.719 INFO  Sensor CSS Rules [javascript] (done) | time=6ms
10:31:06.719 INFO  Sensor Analyzer all Java files [example]
10:31:07.029 INFO  EXECUTION FAILURE
10:31:07.033 INFO  Total time: 2:42.421s
10:31:07.034 ERROR Error during SonarScanner CLI execution
java.lang.NoClassDefFoundError: com/sun/source/tree/Tree
	at org.sonarsource.plugins.custom.rules.JavaCodeFormatRule.execute(JavaCodeFormatRule.java:32)
	at org.sonarsource.plugins.custom.rules.JavaFilesSensor.lambda$execute$0(JavaFilesSensor.java:58)
	at java.base/java.util.HashMap$Values.forEach(Unknown Source)
	at org.sonarsource.plugins.custom.rules.JavaFilesSensor.execute(JavaFilesSensor.java:58)
	at org.sonar.scanner.sensor.AbstractSensorWrapper.analyse(AbstractSensorWrapper.java:64)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:88)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.lambda$execute$1(ModuleSensorsExecutor.java:61)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.withModuleStrategy(ModuleSensorsExecutor.java:79)
	at org.sonar.scanner.sensor.ModuleSensorsExecutor.execute(ModuleSensorsExecutor.java:61)
	at org.sonar.scanner.scan.SpringModuleScanContainer.doAfterStart(SpringModuleScanContainer.java:82)
	at org.sonar.core.platform.SpringComponentContainer.startComponents(SpringComponentContainer.java:188)
	at org.sonar.core.platform.SpringComponentContainer.execute(SpringComponentContainer.java:167)
	at org.sonar.scanner.scan.SpringProjectScanContainer.scan(SpringProjectScanContainer.java:403)
	at org.sonar.scanner.scan.SpringProjectScanContainer.scanRecursively(SpringProjectScanContainer.java:399)
	at org.sonar.scanner.scan.SpringProjectScanContainer.doAfterStart(SpringProjectScanContainer.java:368)
	at org.sonar.core.platform.SpringComponentContainer.startComponents(SpringComponentContainer.java:188)
	at org.sonar.core.platform.SpringComponentContainer.execute(SpringComponentContainer.java:167)
	at org.sonar.scanner.bootstrap.SpringGlobalContainer.doAfterStart(SpringGlobalContainer.java:137)
	at org.sonar.core.platform.SpringComponentContainer.startComponents(SpringComponentContainer.java:188)
	at org.sonar.core.platform.SpringComponentContainer.execute(SpringComponentContainer.java:167)
	at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:72)
	at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:66)
	at org.sonarsource.scanner.lib.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:41)
	at java.base/jdk.internal.reflect.NativeMethod

And I also tried to add --add-opens=jdk.compiler/com.sun.source=ALL-UNNAMED to the environment variables SONAR_WEB_JAVAADDITIONALOPTS and SONAR_CE_JAVAADDITIONALOPTS of sonarqube’s docker deployment, but it still doesn’t work

Hey @cglcl,

Welcome to the community! From the snippet you shared, it is hard to tell what may be the source of the problem.

Did you start your custom rule plugin from a specific template? Is your plugin based on the java-custom-rules template?

Cheers,

Dorian

I am based on this template https://github.com/SonarSource/sonar-custom-plugin-example/tree/9.x

Hello @cglcl,

A few questions about your execution environment:

  • Which JRE are you using to run the scanner?
  • Which scanner version are you using?
  • What is your scanner command?

And you can try passing --add-opens=jdk.compiler/com.sun.source=ALL-UNNAMED in the environment variable SONAR_SCANNER_JAVA_OPTS before running the scanner.
See documentation.

Is the plugin running in the scanner?

  1. I am using jdk8 to run the scanner.
  2. Scanner version:
10:10:29.586 INFO  Scanner configuration file: /work/sonarqub-scanner/sonar-scanner/conf/sonar-scanner.properties
10:10:29.600 INFO  Project root configuration file: NONE
10:10:29.640 INFO  SonarScanner CLI 6.2.1.4610
10:10:29.644 INFO  Java 17.0.12 Eclipse Adoptium (64-bit)
10:10:29.645 INFO  Linux 3.10.0-1160.el7.x86_64 amd64
  1. Like this:
SONAR_CMD=(
  "/work/sonarqub-scanner/sonar-scanner/bin/sonar-scanner"
  "-Dsonar.projectKey=${SONAR_PROJECT_KEY}"
  "-Dsonar.projectName=${SONAR_PROJECT_NAME}"
  "-Dsonar.branch.name=${CI_COMMIT_REF_NAME}"
  "-Dsonar.language=java"
  "-Dsonar.java.source=1.8"
  "-Dsonar.projectVersion=${SONAR_PROJECT_VERSION}"
  "-Dsonar.modules=$(IFS=,; echo "${MODULES[*]}")"
  "${MODULE_PARAMS[@]}"
)

"${SONAR_CMD[@]}"

@eric.giffon
My script

MODULES=()

mvn clean package -Dmaven.test.skip=true -Dxjar.password=io.xjar
mvn dependency:copy-dependencies -DoutputDirectory=./tmplib

for module_dir in */; do
  if [ -d "${module_dir}src/main/java" ]; then
    MODULE_NAME=$(basename "$module_dir")
    MODULES+=("$MODULE_NAME")

    if [ ! -d "${module_dir}target" ]; then
      echo "Target directory does not exist for module $MODULE_NAME"
      continue
    fi

    echo "Current directory: $(pwd)"
    echo "Looking for JAR files in: ${module_dir}target"

    JAR_FILE=$(find "${module_dir}target" -maxdepth 1 -name "*.jar" | head -n 1)
    if [ -z "$JAR_FILE" ]; then
      echo "No JAR file found for module $MODULE_NAME"
      continue
    fi

    for target_module_dir in */; do
      if [ "$target_module_dir" != "$module_dir" ]; then
        mkdir -p "${target_module_dir}tmplib"
        echo "Copy ${JAR_FILE} to ${target_module_dir}tmplib/"
        cp -f "$JAR_FILE" "${target_module_dir}tmplib/"
      fi
    done
  fi
done

MODULE_PARAMS=()
for module_dir in */; do
  if [ -d "$module_dir/src/main/java" ]; then
    MODULE_NAME=$(basename "$module_dir")

    MODULE_PARAMS+=(
#      "-D${MODULE_NAME}.sonar.projectBaseDir=${module_dir}"
      "-D${MODULE_NAME}.sonar.sources=src/main/java"
      "-D${MODULE_NAME}.sonar.java.binaries=target/classes"
    )

    if [ -d "${module_dir}tmplib" ]; then
      echo "Found for module ${MODULE_NAME} tmplib"
      LIBRARIES=$(find "${module_dir}tmplib" -name "*.jar" | sed "s|^${module_dir}||" | tr '\n' ',')
      MODULE_PARAMS+=("-D${MODULE_NAME}.sonar.java.libraries=${LIBRARIES}")
    fi
  fi
done

SONAR_CMD=(
  "/work/sonarqub-scanner/sonar-scanner/bin/sonar-scanner"
  "-Dsonar.projectKey=${SONAR_PROJECT_KEY}"
  "-Dsonar.projectName=${SONAR_PROJECT_NAME}"
  "-Dsonar.branch.name=${CI_COMMIT_REF_NAME}"
  "-Dsonar.language=java"
  "-Dsonar.java.source=1.8"
  "-Dsonar.projectVersion=${SONAR_PROJECT_VERSION}"
  "-Dsonar.modules=$(IFS=,; echo "${MODULES[*]}")"
  "${MODULE_PARAMS[@]}"
)

"${SONAR_CMD[@]}"

Yes, the plugin is running in the scanner.
The scanner version you are using is providing a JRE from the server, which is Java 17 as you can see in the logs.

I looked at the documentation of google-java-format, and it says to add the JVM options as you suggested.

--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

Did you try setting them in SONAR_SCANNER_JAVA_OPTS?

@eric.giffon
I upgraded the jdk of the scanner environment to 21 and added SONAR_SCANNER_JAVA_OPTS but still got the same error.
My script

#!/bin/bash
pwd

ifconfig

echo "Running SonarQube Scanner in directory: $(pwd)"

if [ -z "$SONAR_USER_HOME" ]; then
  echo "Error: Environment variable SONAR_USER_HOME is not set." >&2
  exit 1
fi

echo "SONAR_USER_HOME is set to: $SONAR_USER_HOME"

# 设置 SonarQube 服务器信息
SONAR_PROJECT_KEY=$CI_PROJECT_NAME
SONAR_PROJECT_NAME=$CI_PROJECT_NAME
SONAR_PROJECT_VERSION="1.0"

## 初始化模块列表
#MODULES=()
#mvn clean package -Dmaven.test.skip=true -Dxjar.password=io.xjar
#mvn dependency:copy-dependencies -DoutputDirectory=./tmplib
#
## 收集 JAR 文件
## 打包每个模块并收集 JAR 文件
#for module_dir in */; do
#  if [ -d "${module_dir}src/main/java" ]; then
#    MODULE_NAME=$(basename "$module_dir")
#    MODULES+=("$MODULE_NAME")
#
#    # 检查 target 目录是否存在
#    if [ ! -d "${module_dir}target" ]; then
#      echo "Target directory does not exist for module $MODULE_NAME"
#      continue
#    fi
#
#    # 输出当前工作目录以调试路径问题
#    echo "Current directory: $(pwd)"
#    echo "Looking for JAR files in: ${module_dir}target"
#
#    # 查找生成的 JAR 文件
#    JAR_FILE=$(find "${module_dir}target" -maxdepth 1 -name "*.jar" | head -n 1)
#    if [ -z "$JAR_FILE" ]; then
#      echo "No JAR file found for module $MODULE_NAME"
#      continue
#    fi
#
#    # 将生成的 JAR 文件复制到所有模块的 tmplib 目录
#    for target_module_dir in */; do
#      if [ "$target_module_dir" != "$module_dir" ]; then
#        mkdir -p "${target_module_dir}tmplib"
#        echo "Copy ${JAR_FILE} to ${target_module_dir}tmplib/"
#        cp -f "$JAR_FILE" "${target_module_dir}tmplib/"
#      fi
#    done
#  fi
#done
#
## 为每个模块设置 SonarQube 参数
#MODULE_PARAMS=()
#for module_dir in */; do
#  if [ -d "$module_dir/src/main/java" ]; then
#    MODULE_NAME=$(basename "$module_dir")
#
#    MODULE_PARAMS+=(
##      "-D${MODULE_NAME}.sonar.projectBaseDir=${module_dir}"
#      "-D${MODULE_NAME}.sonar.sources=src/main/java"
#      "-D${MODULE_NAME}.sonar.java.binaries=target/classes"
#    )
#
#    # 查找并合并 tmplib 目录中的所有 JAR 文件
#    if [ -d "${module_dir}tmplib" ]; then
#      echo "Found for module ${MODULE_NAME} tmplib"
#      LIBRARIES=$(find "${module_dir}tmplib" -name "*.jar" | sed "s|^${module_dir}||" | tr '\n' ',')
#      MODULE_PARAMS+=("-D${MODULE_NAME}.sonar.java.libraries=${LIBRARIES}")
#    fi
#  fi
#done

# 构建 SonarQube Scanner 命令
SONAR_CMD=(
  "/work/sonarqub-scanner/sonar-scanner/bin/sonar-scanner"
  "-Dsonar.projectKey=${SONAR_PROJECT_KEY}"
  "-Dsonar.projectName=${SONAR_PROJECT_NAME}"
  "-Dsonar.branch.name=${CI_COMMIT_REF_NAME}"
  "-Dsonar.language=java"
  "-Dsonar.java.source=1.8"
  "-Dsonar.projectVersion=${SONAR_PROJECT_VERSION}"
  "-Dsonar.java.binaries=."
#  "-Dsonar.modules=$(IFS=,; echo "${MODULES[*]}")"
  "-Dsonar.scanner.javaOpts=--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
#  "${MODULE_PARAMS[@]}"
)


"${SONAR_CMD[@]}"