Skip to content

SONARJAVA-6518 Prevent JAVA analysis failure when missing java binaries#5712

Open
alban-auzeill wants to merge 1 commit into
masterfrom
alban/SONARJAVA-6518
Open

SONARJAVA-6518 Prevent JAVA analysis failure when missing java binaries#5712
alban-auzeill wants to merge 1 commit into
masterfrom
alban/SONARJAVA-6518

Conversation

@alban-auzeill

@alban-auzeill alban-auzeill commented Jun 29, 2026

Copy link
Copy Markdown
Member

Note sonar-java classpath properties is used bellow to reference sonar.java.binaries, sonar.java.libraries, sonar.java.test.binaries, sonar.java.test.libraries.

Technical decision part of this PR:

  • All the logic about properties "sonar.binaries" and "sonar.libraries" has been removed from the Java analyzer code.

    • 2014.10.17 Deprecation of the properties with a warning: "sonar.binaries and sonar.libraries are deprecated since version 2.5 of sonar-java-plugin"
    • 2016.05.10 In the PR SONARJAVA-1689 Migrate to LTS #845, if the properties are present, the analysis fails with an exception: sonar.binaries and sonar.libraries are not supported since version 4.0 of sonar-java-plugin
    • Design decision of today: the code has been cleaned from those ancestral properties logic, the analysis does not fail if the properties are present, the user still has warnings related to the missing 'sonar.java.binaries', 'sonar.java.libraries', 'sonar.java.test.binaries', 'sonar.java.test.libraries'
  • An invalid element in one of the sonar-java classpath properties:

    • Will not throw any more IllegalStateException, e.g.: No files nor directories matching 'lib/*.jar'
    • But only a console log and SQ warning. e.g.: Invalid value for 'sonar.java.libraries', no files nor directories matching 'lib/*.jar'.
  • Empty or missing sonar.java.libraries sonar.java.test.libraries:

    • Will not log any more this warning message, e.g.: Dependencies/libraries were not provided for analysis of SOURCE files. The 'sonar.java.libraries' property is empty. Verify your configuration, as you might end up with less precise results.
    • But a console log warning and SQ warning when the property is missing e.g.: Missing 'sonar.java.libraries' property. You might end up with less precise analysis results.
  • Empty or missing sonar.java.binaries:

    • Will not throw any more IllegalStateException, e.g.: Your project contains .java files, please provide compiled classes with sonar.java.binaries property, or exclude them from the analysis with sonar.exclusions property.
    • But a console log warning and SQ warning when the property is missing e.g.: Missing 'sonar.java.binaries' property. You might end up with less precise analysis results.
  • The logic in logUndefinedTypes() has not changed. Warning related to sonar-java classpath properties are only logged/sent if problemsToFilePaths is not empty. So if there were no unresolved imports/types during the analysis, then there are no warnings.

  • There is no longer a need to have a dedicated logic for SonarLint; the class ClasspathForMainForSonarLint has been removed.


Summary by Gitar

  • Refactored classpath initialization:
    • Consolidated classpath logic into AbstractClasspath, introducing a unified init() method and logClasspathWarnings() to handle configuration validation.
    • Replaced hard-failing IllegalStateException with non-blocking analysis warnings when binaries or libraries properties are missing or invalid.
  • Improved user experience:
    • Analysis no longer fails when required classpath properties are missing or invalid, allowing for partial analysis results instead.
    • Updated integration tests to assert successful analysis completion with associated warning logs.
  • Cleaned up codebase:
    • Removed ClasspathForMainForSonarLint and streamlined ClasspathForMain/ClasspathForTest configurations.
    • Standardized property resolution and warning reporting across all classpath components.

This will update automatically on new commits.

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6518

Comment on lines +140 to +154
private Optional<File> existingDirectoryOrLog(String path) {
LOG.debug("Property '{}' set with: {}", ClasspathProperties.SONAR_JAVA_JDK_HOME, path);
File file = new File(path);
if (!file.exists() || !file.isDirectory()) {
LOG.warn("Invalid value for '{}' property, defaulting to runtime JDK.{}Configured location does not exists: '{}'",
ClasspathProperties.SONAR_JAVA_JDK_HOME, System.lineSeparator(), file.getAbsolutePath());
classpathWarnings.add(String.format("Invalid value '%s' for '%s' property, defaulting to runtime JDK.", file.getAbsolutePath(), ClasspathProperties.SONAR_JAVA_JDK_HOME));
return Optional.empty();
}
return Optional.of(file);
}

protected abstract void init();

public abstract void logSuspiciousEmptyLibraries();
public void logClasspathWarnings() {
for (String warning : classpathWarnings) {
LOG.warn(warning);
analysisWarnings.addUnique(warning);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Classpath warnings dropped when no undefined types are reported

All classpath diagnostics are now accumulated into classpathWarnings and only flushed by AbstractClasspath.logClasspathWarnings(). The only production call site is SonarComponents.logUndefinedTypes() (java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-624), but the two logClasspathWarnings() calls are placed after the early-return guard:

public void logUndefinedTypes() {
  if (problemsToFilePaths.isEmpty()) {
    return;            // <-- returns before warnings are flushed
  }
  javaClasspath.logClasspathWarnings();
  ...
}

Consequently, whenever an analysis produces no undefined types, every accumulated classpath warning is silently discarded — including warnings that were previously surfaced unconditionally. In particular, the invalid sonar.java.jdkHome warning used to be emitted immediately via LOG.warn(...) inside existingDirectoryOrLog (see the removed code in AbstractClasspath.java:140-148); it is now deferred into classpathWarnings and will not be logged at all if problemsToFilePaths is empty. The same applies to the new 'Missing sonar.java.binaries/libraries' and 'Invalid value for ...' warnings (AbstractClasspath.java:114-119,170,144). This is a regression in user-facing diagnostics introduced by this PR.

Fix: flush the classpath warnings before the isEmpty() early-return so they are always emitted regardless of undefined types.

Move the classpath-warning flush above the early-return guard so warnings are always emitted.:

public void logUndefinedTypes() {
  javaClasspath.logClasspathWarnings();
  if (!isAutoScan()) {
    // In autoscan, test + main code are analyzed in the same batch ...
    javaTestClasspath.logClasspathWarnings();
  }
  if (problemsToFilePaths.isEmpty()) {
    return;
  }
  logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);
  // clear the set so only new undefined types will be logged
  problemsToFilePaths.clear();
}
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@alban-auzeill alban-auzeill force-pushed the alban/SONARJAVA-6518 branch from 4ae2138 to 4baea4c Compare June 29, 2026 12:21
Comment on lines +150 to +156
public void logClasspathWarnings() {
for (String warning : classpathWarnings) {
LOG.warn(warning);
analysisWarnings.addUnique(warning);
}
classpathWarnings.clear();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Bug: Classpath warnings dropped when no undefined types collected

This PR converts fatal classpath errors (missing/invalid sonar.java.binaries, sonar.java.libraries, invalid sonar.java.jdkHome, and the new "Missing properties" notices) into warnings accumulated in AbstractClasspath.classpathWarnings and flushed by logClasspathWarnings().

However, the only production caller of logClasspathWarnings() is SonarComponents.logUndefinedTypes() (SonarComponents.java:619, 623), which begins with an early return:

public void logUndefinedTypes() {
  if (problemsToFilePaths.isEmpty()) {
    return;
  }
  javaClasspath.logClasspathWarnings();
  ...
}

logUndefinedTypes() is invoked once at the end of analysis from JavaAstScanner.endOfAnalysis() (JavaAstScanner.java:143). When no undefined types were collected during the scan, the method returns before reaching logClasspathWarnings(), so every accumulated classpath warning is silently discarded.

This defeats the intent of the fix: a project that supplies an invalid sonar.java.binaries directory (or omits the property) but whose types all happen to resolve (e.g. from the JDK) will get no warning at all — neither in the logs nor in the SonarQube analysis warnings. Previously such cases failed loudly; now they can be silently ignored.

Suggested fix: flush the classpath warnings unconditionally, before the early return based on problemsToFilePaths. Since logClasspathWarnings() clears its own state, calling it unconditionally is safe.

Flush classpath warnings before the empty-problems early return so they are always emitted.:

public void logUndefinedTypes() {
  javaClasspath.logClasspathWarnings();
  if (!isAutoScan()) {
    javaTestClasspath.logClasspathWarnings();
  }
  if (problemsToFilePaths.isEmpty()) {
    return;
  }
  logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);
  // clear the set so only new undefined types will be logged
  problemsToFilePaths.clear();
}
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@alban-auzeill alban-auzeill force-pushed the alban/SONARJAVA-6518 branch from 4baea4c to 8fd25a0 Compare June 29, 2026 12:34
@alban-auzeill alban-auzeill force-pushed the alban/SONARJAVA-6518 branch from 8fd25a0 to 64f0718 Compare June 29, 2026 13:43
@gitar-bot

gitar-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 0 resolved / 2 findings

Refactors classpath initialization to allow partial analysis without fatal failures, but introduces a regression where classpath warnings are incorrectly dropped when no undefined types are reported.

⚠️ Bug: Classpath warnings dropped when no undefined types are reported

📄 java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-625 📄 java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:140-154 📄 java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-629 📄 java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:144 📄 java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:150-156 📄 java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:170

All classpath diagnostics are now accumulated into classpathWarnings and only flushed by AbstractClasspath.logClasspathWarnings(). The only production call site is SonarComponents.logUndefinedTypes() (java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-624), but the two logClasspathWarnings() calls are placed after the early-return guard:

public void logUndefinedTypes() {
  if (problemsToFilePaths.isEmpty()) {
    return;            // <-- returns before warnings are flushed
  }
  javaClasspath.logClasspathWarnings();
  ...
}

Consequently, whenever an analysis produces no undefined types, every accumulated classpath warning is silently discarded — including warnings that were previously surfaced unconditionally. In particular, the invalid sonar.java.jdkHome warning used to be emitted immediately via LOG.warn(...) inside existingDirectoryOrLog (see the removed code in AbstractClasspath.java:140-148); it is now deferred into classpathWarnings and will not be logged at all if problemsToFilePaths is empty. The same applies to the new 'Missing sonar.java.binaries/libraries' and 'Invalid value for ...' warnings (AbstractClasspath.java:114-119,170,144). This is a regression in user-facing diagnostics introduced by this PR.

Fix: flush the classpath warnings before the isEmpty() early-return so they are always emitted regardless of undefined types.

Move the classpath-warning flush above the early-return guard so warnings are always emitted.
public void logUndefinedTypes() {
  javaClasspath.logClasspathWarnings();
  if (!isAutoScan()) {
    // In autoscan, test + main code are analyzed in the same batch ...
    javaTestClasspath.logClasspathWarnings();
  }
  if (problemsToFilePaths.isEmpty()) {
    return;
  }
  logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);
  // clear the set so only new undefined types will be logged
  problemsToFilePaths.clear();
}
⚠️ Bug: Classpath warnings dropped when no undefined types collected

📄 java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-629 📄 java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:150-156

This PR converts fatal classpath errors (missing/invalid sonar.java.binaries, sonar.java.libraries, invalid sonar.java.jdkHome, and the new "Missing properties" notices) into warnings accumulated in AbstractClasspath.classpathWarnings and flushed by logClasspathWarnings().

However, the only production caller of logClasspathWarnings() is SonarComponents.logUndefinedTypes() (SonarComponents.java:619, 623), which begins with an early return:

public void logUndefinedTypes() {
  if (problemsToFilePaths.isEmpty()) {
    return;
  }
  javaClasspath.logClasspathWarnings();
  ...
}

logUndefinedTypes() is invoked once at the end of analysis from JavaAstScanner.endOfAnalysis() (JavaAstScanner.java:143). When no undefined types were collected during the scan, the method returns before reaching logClasspathWarnings(), so every accumulated classpath warning is silently discarded.

This defeats the intent of the fix: a project that supplies an invalid sonar.java.binaries directory (or omits the property) but whose types all happen to resolve (e.g. from the JDK) will get no warning at all — neither in the logs nor in the SonarQube analysis warnings. Previously such cases failed loudly; now they can be silently ignored.

Suggested fix: flush the classpath warnings unconditionally, before the early return based on problemsToFilePaths. Since logClasspathWarnings() clears its own state, calling it unconditionally is safe.

Flush classpath warnings before the empty-problems early return so they are always emitted.
public void logUndefinedTypes() {
  javaClasspath.logClasspathWarnings();
  if (!isAutoScan()) {
    javaTestClasspath.logClasspathWarnings();
  }
  if (problemsToFilePaths.isEmpty()) {
    return;
  }
  logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);
  // clear the set so only new undefined types will be logged
  problemsToFilePaths.clear();
}
🤖 Prompt for agents
Code Review: Refactors classpath initialization to allow partial analysis without fatal failures, but introduces a regression where classpath warnings are incorrectly dropped when no undefined types are reported.

1. ⚠️ Bug: Classpath warnings dropped when no undefined types are reported
   Files: java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-625, java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:140-154, java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-629, java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:144, java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:150-156, java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:170

   All classpath diagnostics are now accumulated into `classpathWarnings` and only flushed by `AbstractClasspath.logClasspathWarnings()`. The only production call site is `SonarComponents.logUndefinedTypes()` (java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-624), but the two `logClasspathWarnings()` calls are placed *after* the early-return guard:
   
   ```
   public void logUndefinedTypes() {
     if (problemsToFilePaths.isEmpty()) {
       return;            // <-- returns before warnings are flushed
     }
     javaClasspath.logClasspathWarnings();
     ...
   }
   ```
   
   Consequently, whenever an analysis produces no undefined types, every accumulated classpath warning is silently discarded — including warnings that were previously surfaced unconditionally. In particular, the invalid `sonar.java.jdkHome` warning used to be emitted immediately via `LOG.warn(...)` inside `existingDirectoryOrLog` (see the removed code in AbstractClasspath.java:140-148); it is now deferred into `classpathWarnings` and will not be logged at all if `problemsToFilePaths` is empty. The same applies to the new 'Missing sonar.java.binaries/libraries' and 'Invalid value for ...' warnings (AbstractClasspath.java:114-119,170,144). This is a regression in user-facing diagnostics introduced by this PR.
   
   Fix: flush the classpath warnings before the `isEmpty()` early-return so they are always emitted regardless of undefined types.

   Fix (Move the classpath-warning flush above the early-return guard so warnings are always emitted.):
   public void logUndefinedTypes() {
     javaClasspath.logClasspathWarnings();
     if (!isAutoScan()) {
       // In autoscan, test + main code are analyzed in the same batch ...
       javaTestClasspath.logClasspathWarnings();
     }
     if (problemsToFilePaths.isEmpty()) {
       return;
     }
     logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);
     // clear the set so only new undefined types will be logged
     problemsToFilePaths.clear();
   }

2. ⚠️ Bug: Classpath warnings dropped when no undefined types collected
   Files: java-frontend/src/main/java/org/sonar/java/SonarComponents.java:615-629, java-frontend/src/main/java/org/sonar/java/classpath/AbstractClasspath.java:150-156

   This PR converts fatal classpath errors (missing/invalid `sonar.java.binaries`, `sonar.java.libraries`, invalid `sonar.java.jdkHome`, and the new "Missing properties" notices) into warnings accumulated in `AbstractClasspath.classpathWarnings` and flushed by `logClasspathWarnings()`.
   
   However, the only production caller of `logClasspathWarnings()` is `SonarComponents.logUndefinedTypes()` (SonarComponents.java:619, 623), which begins with an early return:
   ```java
   public void logUndefinedTypes() {
     if (problemsToFilePaths.isEmpty()) {
       return;
     }
     javaClasspath.logClasspathWarnings();
     ...
   }
   ```
   `logUndefinedTypes()` is invoked once at the end of analysis from `JavaAstScanner.endOfAnalysis()` (JavaAstScanner.java:143). When no undefined types were collected during the scan, the method returns before reaching `logClasspathWarnings()`, so every accumulated classpath warning is silently discarded.
   
   This defeats the intent of the fix: a project that supplies an invalid `sonar.java.binaries` directory (or omits the property) but whose types all happen to resolve (e.g. from the JDK) will get no warning at all — neither in the logs nor in the SonarQube analysis warnings. Previously such cases failed loudly; now they can be silently ignored.
   
   Suggested fix: flush the classpath warnings unconditionally, before the early return based on `problemsToFilePaths`. Since `logClasspathWarnings()` clears its own state, calling it unconditionally is safe.

   Fix (Flush classpath warnings before the empty-problems early return so they are always emitted.):
   public void logUndefinedTypes() {
     javaClasspath.logClasspathWarnings();
     if (!isAutoScan()) {
       javaTestClasspath.logClasspathWarnings();
     }
     if (problemsToFilePaths.isEmpty()) {
       return;
     }
     logUndefinedTypes(LOGGED_MAX_NUMBER_UNDEFINED_TYPES);
     // clear the set so only new undefined types will be logged
     problemsToFilePaths.clear();
   }

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.
Unblock → Override a blocking verdict and allow merging.

Comment with these commands to change:

Auto-apply Compact Unblock
gitar auto-apply:on         
gitar display:verbose         
gitar unblock         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

Copy link
Copy Markdown

@antoine-vinot-sonarsource antoine-vinot-sonarsource left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of minor comments, otherwise LGTM, I was able to test it locally and it works well.
Also I did not check the Gitar comments, not sure if they are relevant

return false;
}

protected boolean hasJavaFiles() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is exactly the same as the hasJavaSources method line 182

}
}

protected void validatePropertiesPresence(String binariesProperty, String librariesProperty) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two parameters are already class attributes, we should be able to drop them, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants