Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.scanner.ScannerSide;
import org.sonar.java.serialization.SpringContextGatheringModelTypeAdapter;
Expand All @@ -46,6 +48,8 @@
@JsonAdapter(SpringContextGatheringModelTypeAdapter.class)
public class SpringContextGatheringModel {

private static final Logger LOG = LoggerFactory.getLogger(SpringContextGatheringModel.class);

public static final class InputFileData {
@Nullable
private InputFile inputFile;
Expand Down Expand Up @@ -105,11 +109,19 @@ public void restoreFrom(SpringContextGatheringModel previous) {
if (restored) {
return;
}
previous.filesData.forEach((moduleKey, moduleData) -> {
Map<String, InputFileData> current = filesData.computeIfAbsent(moduleKey, key -> new HashMap<>());
moduleData.forEach(current::putIfAbsent);
});
int beanCount = 0;
int packageCount = 0;
for (var moduleEntry : previous.filesData.entrySet()) {
Map<String, InputFileData> current = filesData.computeIfAbsent(moduleEntry.getKey(), key -> new HashMap<>());
for (var fileEntry : moduleEntry.getValue().entrySet()) {
if (current.putIfAbsent(fileEntry.getKey(), fileEntry.getValue()) == null) {
beanCount += fileEntry.getValue().beans().size();
packageCount += fileEntry.getValue().packages().size();
}
}
}
restored = true;
LOG.info("Restored Spring context: {} modules, {} beans, {} packages.", previous.filesData.size(), beanCount, packageCount);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ static Path modelPath(SensorContext context, File rootDirectory) {

static SpringContextGatheringModel load(Path path) {
if (!Files.exists(path)) {
LOG.warn("Spring context model file does not exist: {}. Starting with an empty Spring context.", path);
return new SpringContextGatheringModel();
Comment on lines 50 to 52

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Quality: WARN fires on every first analysis and every non-Spring Java project

JavaSensor.execute calls SpringContextModelPersistence.load(path) on every Java analysis, with nothing checking whether the project uses Spring. The default path is .sonar/spring-context-model.json, which won't exist on a project's first analysis, on CI agents that don't cache .sonar/, or for any project where the model was never written. In all of those cases the new line logs at WARN even though nothing is wrong, so users see warnings they can't act on and real warnings get harder to spot. Log this expected state at INFO or DEBUG. Keep WARN for the read/parse failure branch below, which is a real problem.

Log the missing file, which is expected, at INFO (or DEBUG) instead of WARN.:

LOG.info("Spring context model file does not exist: {}. Starting with an empty Spring context.", path);
  • Apply fix

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

}
try (var reader = Files.newBufferedReader(path, UTF_8)) {
SpringContextGatheringModel model = GSON.fromJson(reader, SpringContextGatheringModel.class);
if (model == null) {
throw new IllegalArgumentException("JSON document is empty");
}
LOG.info("Restoring model from file {}.", path);
return model;
} catch (IOException | RuntimeException e) {
LOG.warn("Unable to load Spring context model from {}, starting from an empty model", path, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.sonar.plugins.java;

import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.SonarProduct;
import org.sonar.api.batch.DependedUpon;
import org.sonar.api.batch.Phase;
Expand Down Expand Up @@ -54,6 +56,8 @@
@DependedUpon(value = "CollectSpringContextBeforeSendingTelemetry")
public class SpringContextModelSensor implements ProjectSensor {

private static final Logger LOG = LoggerFactory.getLogger(SpringContextModelSensor.class);

private final Telemetry telemetry;
private final SpringContextGatheringModel gatheringModel;

Expand All @@ -77,7 +81,14 @@ public void describe(SensorDescriptor descriptor) {
@Override
public void execute(SensorContext context) {
if (isFullProjectAnalysis(context)) {
long unvisitedFileCount = gatheringModel.filesData().values().stream()
.flatMap(files -> files.values().stream())
.filter(data -> data.inputFile() == null)
.count();
gatheringModel.removeUnvisitedFiles();
if (unvisitedFileCount > 0) {
LOG.warn("Removed {} unvisited files from the Spring context gathering model during full project analysis.", unvisitedFileCount);
}
}
long buildingStartTime = System.nanoTime();
SpringContextModel springContextModel = SpringContextModel.of(gatheringModel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ void removes_unvisited_files_before_building_and_saving_the_model_on_full_analys

assertThat(context.allIssues()).isEmpty();
assertThat(gatheringModel.filesData().get(MODULE_KEY)).containsOnlyKeys("currentService", "consumer");
assertThat(logTester.logs(Level.WARN))
.contains("Removed 1 unvisited files from the Spring context gathering model during full project analysis.");
assertThat(telemetry.toMap())
.containsEntry("java.spring.bean_count", "2")
.containsEntry("java.spring.component_scan_package_count", "0");
Expand Down
Loading