diff --git a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextGatheringModel.java b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextGatheringModel.java index aeb60261e56..e33a5820e35 100644 --- a/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextGatheringModel.java +++ b/java-frontend/src/main/java/org/sonar/java/model/springcontext/SpringContextGatheringModel.java @@ -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; @@ -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; @@ -105,11 +109,19 @@ public void restoreFrom(SpringContextGatheringModel previous) { if (restored) { return; } - previous.filesData.forEach((moduleKey, moduleData) -> { - Map current = filesData.computeIfAbsent(moduleKey, key -> new HashMap<>()); - moduleData.forEach(current::putIfAbsent); - }); + int beanCount = 0; + int packageCount = 0; + for (var moduleEntry : previous.filesData.entrySet()) { + Map 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); } /** diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelPersistence.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelPersistence.java index d2cd0bd4fa0..7c1e6a62802 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelPersistence.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelPersistence.java @@ -48,6 +48,7 @@ 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(); } try (var reader = Files.newBufferedReader(path, UTF_8)) { @@ -55,6 +56,7 @@ static SpringContextGatheringModel load(Path path) { 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); diff --git a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java index 3f15140e577..f826735886f 100644 --- a/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java +++ b/sonar-java-plugin/src/main/java/org/sonar/plugins/java/SpringContextModelSensor.java @@ -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; @@ -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; @@ -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); diff --git a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SpringContextModelSensorTest.java b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SpringContextModelSensorTest.java index 3424f8a8387..6a4e29fe382 100644 --- a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SpringContextModelSensorTest.java +++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SpringContextModelSensorTest.java @@ -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");