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 @@ -373,10 +373,8 @@ private static SpringContextModel buildModel(List<String> filePaths) {
List<File> classpath = TestClasspathUtils.DEFAULT_MODULE.getClassPath();
SonarComponents sonarComponents = new SonarComponents(null, null, null, null, null, null);
sonarComponents.setSensorContext(SensorContextTester.create(new File("")));
SpringContextModel model = new SpringContextModel();
sonarComponents.setSpringContextModel(model);

BeanDefinitionGatherer gatherer = new BeanDefinitionGatherer(new SpringContextGatheringModel(), new NoOpTelemetry());
var gatheringModel = new SpringContextGatheringModel();
BeanDefinitionGatherer gatherer = new BeanDefinitionGatherer(gatheringModel, new NoOpTelemetry());
VisitorsBridge visitorsBridge = new VisitorsBridge(List.of((JavaCheck) gatherer), classpath, sonarComponents);
for (String filePath : filePaths) {
File file = new File(filePath);
Expand All @@ -385,7 +383,7 @@ private static SpringContextModel buildModel(List<String> filePaths) {
visitorsBridge.visitFile(compilationUnit, false);
}
visitorsBridge.endOfAnalysis();
return model;
return SpringContextModel.of(gatheringModel);
}

private static InputFile inputFile(File file) {
Expand Down
15 changes: 4 additions & 11 deletions java-frontend/src/main/java/org/sonar/java/JavaFrontend.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@
import org.sonar.java.filters.SonarJavaIssueFilter;
import org.sonar.java.model.JParserConfig;
import org.sonar.java.model.VisitorsBridge;
import org.sonar.java.model.springcontext.SpringContextGatheringModel;
import org.sonar.java.model.springcontext.SpringContextModelGatherers;
import org.sonar.java.telemetry.Telemetry;
import org.sonar.java.telemetry.TelemetryKey;
import org.sonar.plugins.java.api.JavaCheck;
Expand All @@ -61,7 +59,9 @@ public class JavaFrontend {
private static final Logger LOG = LoggerFactory.getLogger(JavaFrontend.class);
private static final String BATCH_ERROR_MESSAGE = "Batch Mode failed, analysis of Java Files stopped.";

/** List of libraries, whose presence or absence we want to report. */
/**
* List of libraries, whose presence or absence we want to report.
*/
private static final Map<TelemetryKey, String> REPORTED_DEPENDENCIES = Map.of(
JAVA_DEPENDENCY_LOMBOK, "lombok",
JAVA_DEPENDENCY_SPRING_BOOT, "spring-boot",
Expand All @@ -77,7 +77,7 @@ public class JavaFrontend {
private final JavaAstScanner astScannerForGeneratedFiles;

public JavaFrontend(JavaVersion javaVersion, SonarComponents sonarComponents, Measurer measurer, Telemetry telemetry,
JavaResourceLocator javaResourceLocator, @Nullable SonarJavaIssueFilter postAnalysisIssueFilter, JavaCheck... visitors) {
JavaResourceLocator javaResourceLocator, @Nullable SonarJavaIssueFilter postAnalysisIssueFilter, JavaCheck... visitors) {
this.javaVersion = javaVersion;
this.sonarComponents = sonarComponents;
this.telemetry = telemetry;
Expand All @@ -96,13 +96,6 @@ public JavaFrontend(JavaVersion javaVersion, SonarComponents sonarComponents, Me
List<JavaCheck> testCodeVisitors = new ArrayList<>(commonVisitors);
testCodeVisitors.add(measurer.new TestFileMeasurer());

if (sonarComponents.getSpringContextModel() != null) {
// Call SpringContextModelGatherers.getAllGatherers twice to have separate gatherer
// instances between the main and test scanners to avoid duplicating the issues
codeVisitors.addAll(SpringContextModelGatherers.getAllGatherers(new SpringContextGatheringModel(), telemetry));
testCodeVisitors.addAll(SpringContextModelGatherers.getAllGatherers(new SpringContextGatheringModel(), telemetry));
}

if (!sonarComponents.isSonarLintContext()) {
codeVisitors.add(new FileLinesVisitor(sonarComponents));
codeVisitors.add(new SyntaxHighlighterVisitor(sonarComponents));
Expand Down
50 changes: 24 additions & 26 deletions java-frontend/src/main/java/org/sonar/java/SonarComponents.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
import org.sonar.java.model.GeneratedFile;
import org.sonar.java.model.JProblem;
import org.sonar.java.model.LineUtils;
import org.sonar.java.model.springcontext.SpringContextModel;
import org.sonar.java.reporting.AnalyzerMessage;
import org.sonar.java.reporting.JavaIssue;
import org.sonar.java.utils.ModuleMetadataUtils;
Expand Down Expand Up @@ -137,7 +136,6 @@ public class SonarComponents extends CheckRegistrar.RegistrarContext {
private SensorContext context;
private UnaryOperator<List<JavaCheck>> checkFilter = UnaryOperator.identity();
private final Set<RuleKey> additionalAutoScanCompatibleRuleKeys;
private SpringContextModel springContextModel;

private boolean alreadyLoggedSkipStatus = false;

Expand Down Expand Up @@ -524,6 +522,11 @@ public File projectLevelWorkDir() {
}
}

public File projectLevelBaseDir() {
var root = ModuleMetadataUtils.getRootProject(projectDefinition);
return root != null ? root.getBaseDir() : fs.baseDir();
}

/**
* Returns an OS-independent key that should identify the module within the project
*
Expand All @@ -545,23 +548,26 @@ public Optional<String> getFullyQualifiedModuleKey() {
public boolean canSkipUnchangedFiles() throws ApiMismatchException {
if (context == null) {
return false;
} else {
var overrideSkipFlag = context.config() == null ? null : context.config().getBoolean(SONAR_CAN_SKIP_UNCHANGED_FILES_KEY).orElse(null);
try {
if (overrideSkipFlag != null) {
return overrideSkipFlag;
}
Method canSkipUnchangedFiles = context.getClass().getMethod("canSkipUnchangedFiles");
return (Boolean) canSkipUnchangedFiles.invoke(context);
} catch (NoSuchMethodError | NoSuchMethodException error) {
throw new ApiMismatchException(error);
} catch (InvocationTargetException | IllegalAccessException error) {
Throwable cause = error.getCause();
if (cause instanceof NoSuchMethodError) {
throw new ApiMismatchException(cause);
}
throw new ApiMismatchException(error);
}
return canSkipUnchangedFiles(context);
}

public static boolean canSkipUnchangedFiles(SensorContext context) throws ApiMismatchException {
var overrideSkipFlag = context.config() == null ? null : context.config().getBoolean(SONAR_CAN_SKIP_UNCHANGED_FILES_KEY).orElse(null);
try {
if (overrideSkipFlag != null) {
return overrideSkipFlag;
}
Method canSkipUnchangedFiles = context.getClass().getMethod("canSkipUnchangedFiles");
return (Boolean) canSkipUnchangedFiles.invoke(context);
} catch (NoSuchMethodError | NoSuchMethodException error) {
throw new ApiMismatchException(error);
} catch (InvocationTargetException | IllegalAccessException error) {
Throwable cause = error.getCause();
if (cause instanceof NoSuchMethodError) {
throw new ApiMismatchException(cause);
}
throw new ApiMismatchException(error);
}
}

Expand Down Expand Up @@ -694,12 +700,4 @@ public Configuration getConfiguration() {
return context.config();
}

public void setSpringContextModel(SpringContextModel springContextModel) {
this.springContextModel = springContextModel;
}

public SpringContextModel getSpringContextModel() {
return springContextModel;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.sonar.api.config.Configuration;
import org.sonar.java.SonarComponents;
import org.sonar.java.caching.CacheContextImpl;
import org.sonar.java.model.springcontext.SpringContextModel;
import org.sonar.java.reporting.AnalyzerMessage;
import org.sonar.java.reporting.FluentReporting;
import org.sonar.java.reporting.InternalJavaIssueBuilder;
Expand Down Expand Up @@ -123,8 +122,4 @@ public Configuration getConfiguration() {
return sonarComponents.getConfiguration();
}

public SpringContextModel getSpringContextModel() {
return sonarComponents.getSpringContextModel();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
package org.sonar.java.model.springcontext;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.java.caching.FileCachingCheck;
import org.sonar.java.model.JUtils;
Expand All @@ -32,7 +29,6 @@
import org.sonar.java.utils.SpringUtils;
import org.sonar.plugins.java.api.InputFileScannerContext;
import org.sonar.plugins.java.api.JavaFileScannerContext;
import org.sonar.plugins.java.api.ModuleScannerContext;
import org.sonar.plugins.java.api.semantic.SymbolMetadata;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.MethodTree;
Expand All @@ -42,8 +38,7 @@
import static org.sonar.java.utils.SpringUtils.collectDependenciesOnMethod;

/**
* Collects Spring bean definitions discovered during AST traversal, and registers them in the
* {@link BeanDefinitionRegistry} of the shared {@link SpringContextModel} at the end of the module analysis.
* Collects Spring bean definitions discovered during AST traversal.
*
* <p>Discovers beans from:
* <ul>
Expand All @@ -64,11 +59,6 @@
* <li>Implicit single-constructor injection (no {@code @Autowired} required)</li>
* </ul>
*
* <p>Also populates:
* <ul>
* <li>{@link TypeToBeansIndex} with the full type hierarchy of each bean</li>
* <li>{@link TypeToDependenciesIndex} with all the dependencies collected by type</li>
* </ul>
*/
public class BeanDefinitionGatherer extends SpringContextModelGatherer
implements FileCachingCheck<List<BeanDefinitionHolder.InputFileData>> {
Expand Down Expand Up @@ -167,42 +157,6 @@ public void restore(InputFileScannerContext context, List<BeanDefinitionHolder.I
springContextGatheringModel.collectBeans(context.getModuleKey(), currentFile.key(), currentFile, beans);
}

/**
* Transfers all beans collected across the module into the shared {@link SpringContextModel}, pairing each
* bean's spans with the file it was collected from to form the {@link BeanLocation}s the model exposes.
* <p>
* Registers all encountered bean definitions in {@link BeanDefinitionRegistry},
* their position in every ancestor/interface type in {@link TypeToBeansIndex}, and
* each of their dependencies by type in {@link TypeToDependenciesIndex}.
*
* @param context Scanner context used here to access the current module key
* @param springContextModel Shared cross-module Spring context
*/
@Override
public void gatherSpringContextData(ModuleScannerContext context, SpringContextModel springContextModel) {
springContextGatheringModel.getInputFilesData(context.getModuleKey()).forEach(inputFileData -> {
InputFile inputFile = inputFileData.inputFile();
for (BeanDefinitionHolder.InputFileData data : inputFileData.beans()) {
var location = new BeanLocation(inputFile, data.textSpan());
var holderBuilder = new BeanDefinitionHolder.Builder(
data.type(), context.getModuleKey(), data.beanPackage(), location)
.dependingBeans(projectToNames(data.dependencies()))
.profileExpression(data.profileExpression())
.qualifier(data.qualifier());
if (data.isPrimary()) {
holderBuilder.primary();
}
springContextModel.getBeanDefinitionRegistry()
.addBeanDefinition(data.beanName(), holderBuilder.build());
for (String typeFqn : data.typeHierarchy()) {
springContextModel.getTypeToBeansIndex().addBeanForType(typeFqn, data.beanName(), context.getModuleKey(), data.beanPackage());
}
data.dependencies().forEach((typeFqn, points) -> points.forEach(point -> springContextModel.getTypeToDependenciesIndex()
.addDependencyForType(typeFqn, point.name(), context.getModuleKey(), data.profileExpression(), new BeanLocation(inputFile, point.span()), point.multiple())));
}
});
}

@Override
protected boolean scanSpringFileWithoutParsing(InputFileScannerContext context) {
return restoreFromCache(context);
Expand Down Expand Up @@ -242,18 +196,4 @@ private void collectBeanMethod(MethodTree method, String pkg, ProfileExpression
}
}

/**
* Projects each type's injection points down to just their names, discarding spans — the flat view stored in {@code BeanDefinitionHolder}.
*
* @param injectionPointsByType Injection points mapped by type, as collected for a bean
* @return The name of each dependency, mapped by type.
*/
private static Map<String, Set<String>> projectToNames(Map<String, Set<InjectionPoint.InputFileData>> injectionPointsByType) {
Map<String, Set<String>> names = new LinkedHashMap<>();
injectionPointsByType.forEach((typeFqn, points) -> names.put(typeFqn, points.stream()
.map(InjectionPoint.InputFileData::name)
.collect(Collectors.toCollection(LinkedHashSet::new))));
return names;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,14 @@
import org.sonar.java.utils.SpringUtils;
import org.sonar.plugins.java.api.InputFileScannerContext;
import org.sonar.plugins.java.api.JavaFileScannerContext;
import org.sonar.plugins.java.api.ModuleScannerContext;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.SymbolMetadata;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonarsource.analyzer.commons.collections.SetUtils;

/**
* Collects packages registered for Spring component scanning and stores them in
* {@link org.sonar.java.model.springcontext.ProjectPackageScan} within the shared
* {@link SpringContextModel}.
* Collects packages registered for Spring component scanning.
*
* <p>Scanned packages are derived from:
* <ul>
Expand All @@ -49,8 +46,7 @@
* <li>{@code @SpringBootApplication} without explicit attributes — the annotated class's own package</li>
* </ul>
*
* <p>Packages are grouped by module and written to {@link org.sonar.java.model.springcontext.ProjectPackageScan}
* at the end of each module's analysis. Per-file results are cached to speed up incremental analyses.
* <p>Per-file results are cached to speed up incremental analyses.
*/
public class ComponentScanPackageGatherer extends SpringContextModelGatherer implements FileCachingCheck<Set<String>> {

Expand Down Expand Up @@ -127,11 +123,6 @@ protected void leaveSpringFile(JavaFileScannerContext context) {
packagesCollectedAtFileLevel.clear();
}

@Override
public void gatherSpringContextData(ModuleScannerContext context, SpringContextModel springContextModel) {
springContextModel.getProjectPackageScan().addPackages(context.getModuleKey(), springContextGatheringModel.getPackages(context.getModuleKey()));
}

private void collectFromComponentScan(SymbolMetadata metadata) {
List<SymbolMetadata.AnnotationValue> componentScanAttributes = metadata.valuesForAnnotation(COMPONENT_SCAN_ANNOTATION);
if (componentScanAttributes == null) {
Expand Down
Loading
Loading