diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2017-07-10 10:38:27 +0200 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2017-07-11 08:51:38 +0200 |
commit | 6f107dcbe90b0fea564e1ecaa96643bfc539329a (patch) | |
tree | 45478253f6006a0f1381fb506f92852ad2f5c5df /sonar-scanner-engine/src/main | |
parent | f6a0f6bb86f7d244bec0b6781020e2ea066124c6 (diff) | |
download | sonarqube-6f107dcbe90b0fea564e1ecaa96643bfc539329a.tar.gz sonarqube-6f107dcbe90b0fea564e1ecaa96643bfc539329a.zip |
SONAR-9477 Deprecate ProjectReactor and ProjectBuilder
Mark Immutable classes in the Plugin API and Scanner
Diffstat (limited to 'sonar-scanner-engine/src/main')
64 files changed, 650 insertions, 472 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java index 0c0b42bef0a..28d1af3517b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java @@ -19,12 +19,12 @@ */ package org.sonar.scanner; -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; +import static java.util.stream.Collectors.toMap; + +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; + import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.SensorContext; @@ -35,10 +35,11 @@ import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.KeyValueFormat.Converter; import org.sonar.scanner.scan.measure.MeasureCache; -import static java.util.stream.Collectors.toMap; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; public class DefaultFileLinesContext implements FileLinesContext { - private final SensorContext context; private final InputFile inputFile; private final MetricFinder metricFinder; @@ -47,7 +48,7 @@ public class DefaultFileLinesContext implements FileLinesContext { /** * metric key -> line -> value */ - private final Map<String, Map<Integer, Object>> map = Maps.newHashMap(); + private final Map<String, Map<Integer, Object>> map = new HashMap<>(); public DefaultFileLinesContext(SensorContext context, InputFile inputFile, MetricFinder metricFinder, MeasureCache measureCache) { this.context = context; @@ -73,13 +74,7 @@ public class DefaultFileLinesContext implements FileLinesContext { public Integer getIntValue(String metricKey, int line) { Preconditions.checkNotNull(metricKey); checkLineRange(line); - - Map<Integer, Object> lines = map.get(metricKey); - if (lines == null) { - // not in memory, so load - lines = loadData(metricKey, KeyValueFormat.newIntegerConverter()); - map.put(metricKey, lines); - } + Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newIntegerConverter())); return (Integer) lines.get(line); } @@ -96,27 +91,13 @@ public class DefaultFileLinesContext implements FileLinesContext { public String getStringValue(String metricKey, int line) { Preconditions.checkNotNull(metricKey); checkLineRange(line); - - Map<Integer, Object> lines = map.get(metricKey); - if (lines == null) { - // not in memory, so load - lines = loadData(metricKey, KeyValueFormat.newStringConverter()); - map.put(metricKey, lines); - } + Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newStringConverter())); return (String) lines.get(line); } - private Map<Integer, Object> getOrCreateLines(String metricKey) { - Map<Integer, Object> lines = map.get(metricKey); - if (lines == null) { - lines = Maps.newHashMap(); - map.put(metricKey, lines); - } - return lines; - } - private void setValue(String metricKey, int line, Object value) { - getOrCreateLines(metricKey).put(line, value); + map.computeIfAbsent(metricKey, k -> new HashMap<>()) + .put(line, value); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java index 40d7cbcd3c7..c0eed526886 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ProjectAnalysisInfo.java @@ -21,6 +21,7 @@ package org.sonar.scanner; import java.util.Date; import java.util.Optional; + import org.picocontainer.Startable; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; @@ -31,6 +32,8 @@ import org.sonar.api.utils.System2; /** * @since 6.3 + * + * Immutable after {@link #start()} */ @ScannerSide public class ProjectAnalysisInfo implements Startable { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java index dc4a860533c..1c0ec317ad7 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/AnalysisTempFolderProvider.java @@ -22,10 +22,11 @@ package org.sonar.scanner.analysis; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; + import org.picocontainer.ComponentLifecycle; import org.picocontainer.PicoContainer; import org.picocontainer.injectors.ProviderAdapter; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.internal.DefaultTempFolder; @@ -34,9 +35,9 @@ public class AnalysisTempFolderProvider extends ProviderAdapter implements Compo private DefaultTempFolder projectTempFolder; private boolean started = false; - public TempFolder provide(ProjectReactor projectReactor) { + public TempFolder provide(InputModuleHierarchy moduleHierarchy) { if (projectTempFolder == null) { - Path workingDir = projectReactor.getRoot().getWorkDir().toPath(); + Path workingDir = moduleHierarchy.root().getWorkDir().toPath(); Path tempDir = workingDir.normalize().resolve(TMP_NAME); try { Files.deleteIfExists(tempDir); @@ -64,7 +65,7 @@ public class AnalysisTempFolderProvider extends ProviderAdapter implements Compo @Override public void dispose(PicoContainer container) { - //nothing to do + // nothing to do } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java index 9414be0017f..28fefb1080a 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/analysis/DefaultAnalysisMode.java @@ -21,6 +21,8 @@ package org.sonar.scanner.analysis; import java.util.Map; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -30,6 +32,7 @@ import org.sonar.scanner.bootstrap.GlobalProperties; /** * @since 4.0 */ +@Immutable public class DefaultAnalysisMode extends AbstractAnalysisMode { private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java index 76b37d87b29..743294be0f8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalMode.java @@ -19,10 +19,13 @@ */ package org.sonar.scanner.bootstrap; +import javax.annotation.concurrent.Immutable; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; +@Immutable public class GlobalMode extends AbstractAnalysisMode { private static final Logger LOG = LoggerFactory.getLogger(GlobalMode.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java index 70749f70fc0..0117f735ba6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerExtensionDictionnary.java @@ -30,6 +30,7 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nullable; + import org.apache.commons.lang.ClassUtils; import org.sonar.api.batch.CheckProject; import org.sonar.api.batch.DependedUpon; @@ -279,7 +280,7 @@ public class ScannerExtensionDictionnary { || (org.sonar.api.batch.Sensor.class.equals(type) && ClassUtils.isAssignable(extension.getClass(), Sensor.class))) && (matcher == null || matcher.accept(extension)); if (keep && module != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) { - keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module.definition())); + keep = ((CheckProject) extension).shouldExecuteOnProject(new Project(module)); } return keep; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java index 87a0794c1e5..e8e1d804b36 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java @@ -19,9 +19,8 @@ */ package org.sonar.scanner.cpd; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Predicate; +import static com.google.common.collect.FluentIterable.from; + import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -30,10 +29,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; + import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputComponent; -import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.duplications.block.Block; @@ -49,7 +48,9 @@ import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.util.ProgressReport; -import static com.google.common.collect.FluentIterable.from; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Predicate; /** * Runs on the root module, at the end of the project analysis. @@ -66,12 +67,12 @@ public class CpdExecutor { private final SonarCpdBlockIndex index; private final ReportPublisher publisher; private final InputComponentStore componentStore; - private final Configuration settings; private final ProgressReport progressReport; + private final CpdSettings settings; private int count; private int total; - public CpdExecutor(Configuration settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) { + public CpdExecutor(CpdSettings settings, SonarCpdBlockIndex index, ReportPublisher publisher, InputComponentStore inputComponentCache) { this.settings = settings; this.index = index; this.publisher = publisher; @@ -139,7 +140,8 @@ public class CpdExecutor { List<CloneGroup> filtered; if (!"java".equalsIgnoreCase(inputFile.language())) { - Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(inputFile.language())); + int minTokens = settings.getMinimumTokens(inputFile.language()); + Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(minTokens); filtered = from(duplications).filter(minimumTokensPredicate).toList(); } else { filtered = duplications; @@ -149,17 +151,6 @@ public class CpdExecutor { } @VisibleForTesting - /** - * Not applicable to Java, as the {@link BlockChunker} that it uses does not record start and end units of each block. - * Also, it uses statements instead of tokens. - * @param languageKey - * @return - */ - int getMinimumTokens(String languageKey) { - return settings.getInt("sonar.cpd." + languageKey + ".minimumTokens").orElse(100); - } - - @VisibleForTesting final void saveDuplications(final DefaultInputComponent component, List<CloneGroup> duplications) { if (duplications.size() > MAX_CLONE_GROUP_PER_FILE) { LOG.warn("Too many duplication groups on file " + component + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdSettings.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdSettings.java new file mode 100644 index 00000000000..7335d85ef88 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdSettings.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.cpd; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.config.Configuration; +import org.sonar.duplications.block.BlockChunker; + +public class CpdSettings { + private final Configuration settings; + private final String branch; + + public CpdSettings(Configuration settings, InputModuleHierarchy hierarchy) { + this.settings = settings; + this.branch = hierarchy.root().getBranch(); + } + + public boolean isCrossProjectDuplicationEnabled() { + return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false) + // No cross project duplication for branches + && StringUtils.isBlank(branch); + } + + /** + * Not applicable to Java, as the {@link BlockChunker} that it uses does not record start and end units of each block. + * Also, it uses statements instead of tokens. + * @param languageKey + * @return + */ + int getMinimumTokens(String languageKey) { + return settings.getInt("sonar.cpd." + languageKey + ".minimumTokens").orElse(100); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java index 2a4cdf9eab0..a5bc77ba3e6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/index/SonarCpdBlockIndex.java @@ -24,10 +24,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.stream.Collectors; -import org.sonar.api.CoreProperties; + import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.duplications.block.Block; @@ -36,6 +35,7 @@ import org.sonar.duplications.index.AbstractCloneIndex; import org.sonar.duplications.index.CloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.protocol.output.FileStructure; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.report.ReportPublisher; @@ -44,17 +44,17 @@ public class SonarCpdBlockIndex extends AbstractCloneIndex { private static final Logger LOG = Loggers.get(SonarCpdBlockIndex.class); private final CloneIndex mem = new PackedMemoryCloneIndex(); private final ReportPublisher publisher; - private final Configuration settings; // Files already tokenized private final Set<InputFile> indexedFiles = new HashSet<>(); + private final CpdSettings settings; - public SonarCpdBlockIndex(ReportPublisher publisher, Configuration settings) { + public SonarCpdBlockIndex(ReportPublisher publisher, CpdSettings settings) { this.publisher = publisher; this.settings = settings; } public void insert(InputFile inputFile, Collection<Block> blocks) { - if (isCrossProjectDuplicationEnabled(settings)) { + if (settings.isCrossProjectDuplicationEnabled()) { int id = ((DefaultInputFile) inputFile).batchId(); if (publisher.getWriter().hasComponentData(FileStructure.Domain.CPD_TEXT_BLOCKS, id)) { throw new UnsupportedOperationException("Trying to save CPD tokens twice for the same file is not supported: " + inputFile.absolutePath()); @@ -87,12 +87,6 @@ public class SonarCpdBlockIndex extends AbstractCloneIndex { return indexedFiles.contains(inputFile); } - public static boolean isCrossProjectDuplicationEnabled(Configuration settings) { - return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT).orElse(false) - // No cross project duplication for branches - && !settings.get(CoreProperties.PROJECT_BRANCH_PROPERTY).isPresent(); - } - public Collection<Block> getByInputFile(String resourceKey) { return mem.getByResourceId(resourceKey); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java index 1690a8a44be..14eb7316b40 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java @@ -123,7 +123,7 @@ public class DefaultIndex { if (component == null) { throw new IllegalStateException("Invalid component key: " + key); } - if (sensorStorage.isDeprecatedMetric(measure.getMetricKey())) { + if (DefaultSensorStorage.isDeprecatedMetric(measure.getMetricKey())) { // Ignore deprecated metrics return measure; } @@ -187,7 +187,7 @@ public class DefaultIndex { } else if (inputComponent instanceof InputFile) { r = File.create(((InputFile) inputComponent).relativePath()); } else if (inputComponent instanceof InputModule) { - r = new Project(((DefaultInputModule) inputComponent).definition()); + r = new Project(((DefaultInputModule) inputComponent)); } else { throw new IllegalArgumentException("Unknow input path type: " + inputComponent); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java index ccc86732f23..a0331d07aa9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultFilterableIssue.java @@ -20,6 +20,9 @@ package org.sonar.scanner.issue; import java.util.Date; + +import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.batch.fs.InputModule; @@ -29,6 +32,7 @@ import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.scanner.ProjectAnalysisInfo; import org.sonar.scanner.protocol.output.ScannerReport.Issue; +@ThreadSafe public class DefaultFilterableIssue implements FilterableIssue { private final Issue rawIssue; private final ProjectAnalysisInfo projectAnalysisInfo; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java index d0f01cc432d..fb8105bfd58 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/DefaultIssueFilterChain.java @@ -22,10 +22,14 @@ package org.sonar.scanner.issue; import java.util.Arrays; import java.util.Collections; import java.util.List; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.scan.issue.filter.FilterableIssue; import org.sonar.api.scan.issue.filter.IssueFilter; import org.sonar.api.scan.issue.filter.IssueFilterChain; +@ThreadSafe public class DefaultIssueFilterChain implements IssueFilterChain { private final List<IssueFilter> filters; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java index db9105a3f90..f31865db0cf 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssueFilters.java @@ -30,14 +30,14 @@ import org.sonar.scanner.protocol.output.ScannerReport; @ScannerSide public class IssueFilters { - private final IssueFilter[] filters; + private final IssueFilterChain filterChain; private final org.sonar.api.issue.batch.IssueFilter[] deprecatedFilters; private final DefaultInputModule module; private final ProjectAnalysisInfo projectAnalysisInfo; public IssueFilters(DefaultInputModule module, ProjectAnalysisInfo projectAnalysisInfo, IssueFilter[] exclusionFilters, org.sonar.api.issue.batch.IssueFilter[] filters) { this.module = module; - this.filters = exclusionFilters; + this.filterChain = new DefaultIssueFilterChain(exclusionFilters); this.deprecatedFilters = filters; this.projectAnalysisInfo = projectAnalysisInfo; } @@ -55,7 +55,6 @@ public class IssueFilters { } public boolean accept(String componentKey, ScannerReport.Issue rawIssue) { - IssueFilterChain filterChain = new DefaultIssueFilterChain(filters); FilterableIssue fIssue = new DefaultFilterableIssue(module, projectAnalysisInfo, rawIssue, componentKey); if (filterChain.accept(fIssue)) { return acceptDeprecated(componentKey, rawIssue); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java index 874e4514cf9..279b003d933 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ModuleIssues.java @@ -19,7 +19,8 @@ */ package org.sonar.scanner.issue; -import com.google.common.base.Strings; +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.batch.rule.ActiveRule; @@ -35,9 +36,12 @@ import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.IssueLocation; import org.sonar.scanner.report.ReportPublisher; +import com.google.common.base.Strings; + /** * Initialize the issues raised during scan. */ +@ThreadSafe public class ModuleIssues { private final ActiveRules activeRules; @@ -62,9 +66,19 @@ public class ModuleIssues { return false; } - String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? rule.name() : issue.primaryLocation().message(); + ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.batchId(), rule.name(), activeRule.severity()); + + if (filters.accept(inputComponent.key(), rawIssue)) { + write(inputComponent.batchId(), rawIssue); + return true; + } + return false; + } + + private static ScannerReport.Issue createReportIssue(Issue issue, int batchId, String ruleName, String activeRuleSeverity) { + String primaryMessage = Strings.isNullOrEmpty(issue.primaryLocation().message()) ? ruleName : issue.primaryLocation().message(); org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity(); - Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRule.severity()); + Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRuleSeverity); ScannerReport.Issue.Builder builder = ScannerReport.Issue.newBuilder(); ScannerReport.IssueLocation.Builder locationBuilder = IssueLocation.newBuilder(); @@ -76,7 +90,7 @@ public class ModuleIssues { builder.setMsg(primaryMessage); locationBuilder.setMsg(primaryMessage); - locationBuilder.setComponentRef(inputComponent.batchId()); + locationBuilder.setComponentRef(batchId); TextRange primaryTextRange = issue.primaryLocation().textRange(); if (primaryTextRange != null) { builder.setTextRange(toProtobufTextRange(textRangeBuilder, primaryTextRange)); @@ -86,13 +100,7 @@ public class ModuleIssues { builder.setGap(gap); } applyFlows(builder, locationBuilder, textRangeBuilder, issue); - ScannerReport.Issue rawIssue = builder.build(); - - if (filters.accept(inputComponent.key(), rawIssue)) { - write(inputComponent.batchId(), rawIssue); - return true; - } - return false; + return builder.build(); } private static void applyFlows(ScannerReport.Issue.Builder builder, ScannerReport.IssueLocation.Builder locationBuilder, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java index 66efeb194f3..dc96c80d2d1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/EnforceIssuesFilter.java @@ -19,7 +19,13 @@ */ package org.sonar.scanner.issue.ignore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.ThreadSafe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputComponent; @@ -31,14 +37,15 @@ import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; import org.sonar.scanner.scan.filesystem.InputComponentStore; +@ThreadSafe public class EnforceIssuesFilter implements IssueFilter { private static final Logger LOG = LoggerFactory.getLogger(EnforceIssuesFilter.class); - private IssueInclusionPatternInitializer patternInitializer; - private InputComponentStore componentStore; + private final List<IssuePattern> multicriteriaPatterns; + private final InputComponentStore componentStore; public EnforceIssuesFilter(IssueInclusionPatternInitializer patternInitializer, InputComponentStore componentStore) { - this.patternInitializer = patternInitializer; + this.multicriteriaPatterns = Collections.unmodifiableList(new ArrayList<>(patternInitializer.getMulticriteriaPatterns())); this.componentStore = componentStore; } @@ -48,7 +55,7 @@ public class EnforceIssuesFilter implements IssueFilter { boolean atLeastOnePatternFullyMatched = false; IssuePattern matchingPattern = null; - for (IssuePattern pattern : patternInitializer.getMulticriteriaPatterns()) { + for (IssuePattern pattern : multicriteriaPatterns) { if (pattern.getRulePattern().match(issue.ruleKey().toString())) { atLeastOneRuleMatched = true; String relativePath = getRelativePath(issue.componentKey()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java index 4d326f7c198..689d4158d8d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/pattern/IssuePattern.java @@ -24,17 +24,20 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.WildcardPattern; +@Immutable public class IssuePattern { private final WildcardPattern resourcePattern; private final WildcardPattern rulePattern; - private final Set<Integer> lines = new LinkedHashSet<>(); - private final Set<LineRange> lineRanges = new LinkedHashSet<>(); + private final Set<Integer> lines; + private final Set<LineRange> lineRanges; private final boolean checkLines; public IssuePattern(String resourcePattern, String rulePattern) { @@ -45,13 +48,19 @@ public class IssuePattern { this.resourcePattern = WildcardPattern.create(resourcePattern); this.rulePattern = WildcardPattern.create(rulePattern); this.checkLines = !lineRanges.isEmpty(); + Set<Integer> modifiableLines = new LinkedHashSet<>(); + Set<LineRange> modifiableLineRanges = new LinkedHashSet<>(); + for (LineRange range : lineRanges) { if (range.from() == range.to()) { - this.lines.add(range.from()); + modifiableLines.add(range.from()); } else { - this.lineRanges.add(range); + modifiableLineRanges.add(range); } } + + this.lines = Collections.unmodifiableSet(modifiableLines); + this.lineRanges = Collections.unmodifiableSet(modifiableLineRanges); } public WildcardPattern getResourcePattern() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java index 4bdf149ec05..df5ce6c6f1d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsLoader.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.CheckForNull; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.fs.internal.FileMetadata.CharHandler; +import org.sonar.api.batch.fs.internal.charhandler.CharHandler; import org.sonar.scanner.issue.ignore.pattern.BlockIssuePattern; import org.sonar.scanner.issue.ignore.pattern.IssueExclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.IssuePattern; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java index e0230681382..3611b6d7ea5 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/ignore/scanner/IssueExclusionsRegexpScanner.java @@ -27,7 +27,7 @@ import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.batch.fs.internal.FileMetadata.CharHandler; +import org.sonar.api.batch.fs.internal.charhandler.CharHandler; import org.sonar.scanner.issue.ignore.pattern.LineRange; import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader.DoubleRegexpMatcher; @@ -57,19 +57,19 @@ public class IssueExclusionsRegexpScanner extends CharHandler { } @Override - protected void handleIgnoreEoL(char c) { + public void handleIgnoreEoL(char c) { sb.append(c); } @Override - protected void newLine() { + public void newLine() { processLine(sb.toString()); sb.setLength(0); lineIndex++; } @Override - protected void eof() { + public void eof() { processLine(sb.toString()); if (currentMatcher != null && !currentMatcher.hasSecondPattern()) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java index 1d659f99c9c..8671b672c74 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/tracking/ServerIssueRepository.java @@ -21,16 +21,16 @@ package org.sonar.scanner.issue.tracking; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.core.component.ComponentKeys; import org.sonar.scanner.protocol.input.ScannerInput.ServerIssue; import org.sonar.scanner.repository.ServerIssuesLoader; -import org.sonar.scanner.scan.ImmutableProjectReactor; import org.sonar.scanner.scan.filesystem.InputComponentStore; import org.sonar.scanner.storage.Storage; import org.sonar.scanner.storage.Storages; @@ -45,21 +45,20 @@ public class ServerIssueRepository { private final Storages caches; private Storage<ServerIssue> issuesCache; private final ServerIssuesLoader previousIssuesLoader; - private final ImmutableProjectReactor reactor; - private final InputComponentStore resourceCache; + private final InputComponentStore componentStore; - public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, ImmutableProjectReactor reactor, InputComponentStore resourceCache) { + public ServerIssueRepository(Storages caches, ServerIssuesLoader previousIssuesLoader, InputComponentStore componentStore) { this.caches = caches; this.previousIssuesLoader = previousIssuesLoader; - this.reactor = reactor; - this.resourceCache = resourceCache; + this.componentStore = componentStore; } public void load() { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); this.issuesCache = caches.createCache("previousIssues"); caches.registerValueCoder(ServerIssue.class, new ServerIssueValueCoder()); - previousIssuesLoader.load(reactor.getRoot().getKeyWithBranch(), this::store); + DefaultInputModule root = (DefaultInputModule) componentStore.root(); + previousIssuesLoader.load(root.getKeyWithBranch(), this::store); profiler.stopInfo(); } @@ -69,10 +68,10 @@ public class ServerIssueRepository { private void store(ServerIssue issue) { String moduleKeyWithBranch = issue.getModuleKey(); - ProjectDefinition projectDefinition = reactor.getProjectDefinition(moduleKeyWithBranch); - if (projectDefinition != null) { - String componentKeyWithoutBranch = ComponentKeys.createEffectiveKey(projectDefinition.getKey(), issue.hasPath() ? issue.getPath() : null); - DefaultInputComponent r = (DefaultInputComponent) resourceCache.getByKey(componentKeyWithoutBranch); + InputModule module = componentStore.getModule(moduleKeyWithBranch); + if (module != null) { + String componentKeyWithoutBranch = ComponentKeys.createEffectiveKey(module.key(), issue.hasPath() ? issue.getPath() : null); + DefaultInputComponent r = (DefaultInputComponent) componentStore.getByKey(componentKeyWithoutBranch); if (r != null) { issuesCache.put(r.batchId(), issue.getKey(), issue); return; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java index 46acfff0279..e4e5c81356e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java @@ -22,6 +22,7 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; @@ -40,9 +41,10 @@ public abstract class AbstractPhaseExecutor { private final DefaultModuleFileSystem fs; private final QProfileVerifier profileVerifier; private final IssueExclusionsLoader issueExclusionsLoader; + private final InputModuleHierarchy hierarchy; public AbstractPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, - SensorContext sensorContext, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, + SensorContext sensorContext, InputModuleHierarchy hierarchy, EventBus eventBus, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader) { this.postJobsExecutor = postJobsExecutor; this.initializersExecutor = initializersExecutor; @@ -53,6 +55,7 @@ public abstract class AbstractPhaseExecutor { this.fs = fs; this.profileVerifier = profileVerifier; this.issueExclusionsLoader = issueExclusionsLoader; + this.hierarchy = hierarchy; } /** @@ -76,7 +79,7 @@ public abstract class AbstractPhaseExecutor { afterSensors(); - if (module.definition().getParent() == null) { + if (hierarchy.isRoot(module)) { executeOnRoot(); postJobsExecutor.execute(sensorContext); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java index b4d5818fc47..556e6298652 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/InitializersExecutor.java @@ -19,8 +19,8 @@ */ package org.sonar.scanner.phases; -import com.google.common.collect.Lists; import java.util.Collection; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.Initializer; import org.sonar.api.batch.fs.internal.DefaultInputModule; @@ -31,6 +31,8 @@ import org.sonar.api.utils.log.Profiler; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.events.EventBus; +import com.google.common.collect.Lists; + public class InitializersExecutor { private static final Logger LOG = Loggers.get(SensorsExecutor.class); @@ -52,7 +54,7 @@ public class InitializersExecutor { LOG.debug("Initializers : {}", StringUtils.join(initializers, " -> ")); } - Project project = new Project(module.definition()); + Project project = new Project(module); for (Initializer initializer : initializers) { eventBus.fireEvent(new InitializerExecutionEvent(initializer, true)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java index 7893e6a0024..e2fd0aea9ff 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java @@ -22,6 +22,7 @@ package org.sonar.scanner.phases; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.issue.IssueCallback; @@ -43,8 +44,8 @@ public final class IssuesPhaseExecutor extends AbstractPhaseExecutor { public IssuesPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, EventBus eventBus, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); + IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback, InputModuleHierarchy moduleHierarchy) { + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, moduleHierarchy, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); this.eventBus = eventBus; this.issuesReport = jsonReport; this.localIssueTracking = localIssueTracking; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java index b3b33277f66..d4b42a5f97c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PostJobsExecutor.java @@ -21,6 +21,7 @@ package org.sonar.scanner.phases; import java.util.ArrayList; import java.util.Collection; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.PostJob; import org.sonar.api.batch.ScannerSide; @@ -58,7 +59,7 @@ public class PostJobsExecutor { private void execute(SensorContext context, Collection<PostJob> postJobs) { logPostJobs(postJobs); - Project project = new Project(module.definition()); + Project project = new Project(module); for (PostJob postJob : postJobs) { LOG.info("Executing post-job {}", ScannerUtils.describe(postJob)); eventBus.fireEvent(new PostJobExecutionEvent(postJob, true)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java index d8a5d9fcac8..63868c8e2cb 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/ProjectAnalysisEvent.java @@ -34,7 +34,7 @@ class ProjectAnalysisEvent extends AbstractPhaseEvent<ProjectAnalysisHandler> @Override public Project getProject() { - return new Project(module.definition()); + return new Project(module); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java index 434c88a96dd..84ff4b4d4bc 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java @@ -20,6 +20,7 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.cpd.CpdExecutor; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; @@ -38,9 +39,9 @@ public final class PublishPhaseExecutor extends AbstractPhaseExecutor { private final ScmPublisher scm; public PublishPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, - EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, - QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); + EventBus eventBus, ReportPublisher reportPublisher, FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, + IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy) { + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, hierarchy, eventBus, fsLogger, fs, profileVerifier, issueExclusionsLoader); this.eventBus = eventBus; this.reportPublisher = reportPublisher; this.cpdExecutor = cpdExecutor; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java index c4b1805d3ba..88158bfb065 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/SensorsExecutor.java @@ -27,6 +27,7 @@ import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.resources.Project; import org.sonar.scanner.bootstrap.ScannerExtensionDictionnary; import org.sonar.scanner.events.EventBus; @@ -40,12 +41,12 @@ public class SensorsExecutor { private final SensorStrategy strategy; private final boolean isRoot; - public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, EventBus eventBus, SensorStrategy strategy) { + public SensorsExecutor(ScannerExtensionDictionnary selector, DefaultInputModule module, InputModuleHierarchy hierarchy, EventBus eventBus, SensorStrategy strategy) { this.selector = selector; this.module = module; this.eventBus = eventBus; this.strategy = strategy; - this.isRoot = module.definition().getParent() == null; + this.isRoot = hierarchy.isRoot(module); } public void execute(SensorContext context) { @@ -84,7 +85,7 @@ public class SensorsExecutor { private void executeSensor(SensorContext context, Sensor sensor) { eventBus.fireEvent(new SensorExecutionEvent(sensor, true)); - sensor.analyse(new Project(module.definition()), context); + sensor.analyse(new Project(module), context); eventBus.fireEvent(new SensorExecutionEvent(sensor, false)); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java index 4da1aeda04e..0ff003378b4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java @@ -29,10 +29,12 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.TreeSet; + import org.sonar.api.CoreProperties; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.utils.System2; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -56,16 +58,18 @@ public class AnalysisContextReportPublisher { private final System2 system; private final ProjectRepositories projectRepos; private final GlobalConfiguration globalSettings; + private final InputModuleHierarchy hierarchy; private ScannerReportWriter writer; public AnalysisContextReportPublisher(AnalysisMode mode, ScannerPluginRepository pluginRepo, System2 system, - ProjectRepositories projectRepos, GlobalConfiguration globalSettings) { + ProjectRepositories projectRepos, GlobalConfiguration globalSettings, InputModuleHierarchy hierarchy) { this.mode = mode; this.pluginRepo = pluginRepo; this.system = system; this.projectRepos = projectRepos; this.globalSettings = globalSettings; + this.hierarchy = hierarchy; } public void init(ScannerReportWriter writer) { @@ -120,15 +124,15 @@ public class AnalysisContextReportPublisher { } } - public void dumpModuleSettings(ProjectDefinition moduleDefinition) { + public void dumpModuleSettings(DefaultInputModule module) { if (mode.isIssues()) { return; } File analysisLog = writer.getFileStructure().analysisLog(); try (BufferedWriter fileWriter = Files.newBufferedWriter(analysisLog.toPath(), StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { - Map<String, String> moduleSpecificProps = collectModuleSpecificProps(moduleDefinition); - fileWriter.append(String.format("Settings for module: %s", moduleDefinition.getKey())).append('\n'); + Map<String, String> moduleSpecificProps = collectModuleSpecificProps(module); + fileWriter.append(String.format("Settings for module: %s", module.key())).append('\n'); for (String prop : new TreeSet<>(moduleSpecificProps.keySet())) { if (isSystemProp(prop) || isEnvVariable(prop) || !isSqProp(prop)) { continue; @@ -147,17 +151,17 @@ public class AnalysisContextReportPublisher { /** * Only keep props that are not in parent */ - private Map<String, String> collectModuleSpecificProps(ProjectDefinition moduleDefinition) { + private Map<String, String> collectModuleSpecificProps(DefaultInputModule module) { Map<String, String> moduleSpecificProps = new HashMap<>(); - if (projectRepos.moduleExists(moduleDefinition.getKeyWithBranch())) { - moduleSpecificProps.putAll(projectRepos.settings(moduleDefinition.getKeyWithBranch())); + if (projectRepos.moduleExists(module.getKeyWithBranch())) { + moduleSpecificProps.putAll(projectRepos.settings(module.getKeyWithBranch())); } - ProjectDefinition parent = moduleDefinition.getParent(); + DefaultInputModule parent = hierarchy.parent(module); if (parent == null) { - moduleSpecificProps.putAll(moduleDefinition.properties()); + moduleSpecificProps.putAll(module.properties()); } else { Map<String, String> parentProps = parent.properties(); - for (Map.Entry<String, String> entry : moduleDefinition.properties().entrySet()) { + for (Map.Entry<String, String> entry : module.properties().entrySet()) { if (!parentProps.containsKey(entry.getKey()) || !parentProps.get(entry.getKey()).equals(entry.getValue())) { moduleSpecificProps.put(entry.getKey(), entry.getValue()); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index 78fa6bea16c..395b6e4b63b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -22,6 +22,7 @@ package org.sonar.scanner.report; import java.util.Collection; import java.util.stream.Collectors; import javax.annotation.CheckForNull; + import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index 9afa52f2ca8..871f65ca0f0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -25,7 +25,7 @@ import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.config.Configuration; import org.sonar.scanner.ProjectAnalysisInfo; -import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.rule.ModuleQProfiles; @@ -37,12 +37,15 @@ public class MetadataPublisher implements ReportPublisherStep { private final ModuleQProfiles qProfiles; private final ProjectAnalysisInfo projectAnalysisInfo; private final InputModuleHierarchy moduleHierarchy; + private final CpdSettings cpdSettings; - public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings, ModuleQProfiles qProfiles) { + public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, Configuration settings, + ModuleQProfiles qProfiles, CpdSettings cpdSettings) { this.projectAnalysisInfo = projectAnalysisInfo; this.moduleHierarchy = moduleHierarchy; this.settings = settings; this.qProfiles = qProfiles; + this.cpdSettings = cpdSettings; } @Override @@ -53,7 +56,7 @@ public class MetadataPublisher implements ReportPublisherStep { .setAnalysisDate(projectAnalysisInfo.analysisDate().getTime()) // Here we want key without branch .setProjectKey(rootDef.getKey()) - .setCrossProjectDuplicationActivated(SonarCpdBlockIndex.isCrossProjectDuplicationEnabled(settings)) + .setCrossProjectDuplicationActivated(cpdSettings.isCrossProjectDuplicationEnabled()) .setRootComponentRef(rootProject.batchId()); settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(builder::setOrganizationKey); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java index 487c48cd4e5..54fd94ca923 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java @@ -19,8 +19,8 @@ */ package org.sonar.scanner.report; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; +import static org.sonar.core.util.FileUtils.deleteQuietly; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -31,13 +31,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.Map; + import javax.annotation.Nullable; -import okhttp3.HttpUrl; + import org.apache.commons.io.FileUtils; import org.picocontainer.Startable; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.config.Configuration; import org.sonar.api.platform.Server; import org.sonar.api.utils.MessageException; @@ -48,14 +49,16 @@ import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.analysis.DefaultAnalysisMode; import org.sonar.scanner.bootstrap.ScannerWsClient; import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.scan.ImmutableProjectReactor; import org.sonarqube.ws.MediaTypes; import org.sonarqube.ws.WsCe; import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.PostRequest; import org.sonarqube.ws.client.WsResponse; -import static org.sonar.core.util.FileUtils.deleteQuietly; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; + +import okhttp3.HttpUrl; @ScannerSide public class ReportPublisher implements Startable { @@ -69,7 +72,7 @@ public class ReportPublisher implements Startable { private final Configuration settings; private final ScannerWsClient wsClient; private final AnalysisContextReportPublisher contextPublisher; - private final ImmutableProjectReactor projectReactor; + private final InputModuleHierarchy moduleHierarchy; private final DefaultAnalysisMode analysisMode; private final TempFolder temp; private final ReportPublisherStep[] publishers; @@ -79,12 +82,12 @@ public class ReportPublisher implements Startable { private ScannerReportWriter writer; public ReportPublisher(Configuration settings, ScannerWsClient wsClient, Server server, AnalysisContextReportPublisher contextPublisher, - ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { + InputModuleHierarchy moduleHierarchy, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { this.settings = settings; this.wsClient = wsClient; this.server = server; this.contextPublisher = contextPublisher; - this.projectReactor = projectReactor; + this.moduleHierarchy = moduleHierarchy; this.analysisMode = analysisMode; this.temp = temp; this.publishers = publishers; @@ -92,7 +95,7 @@ public class ReportPublisher implements Startable { @Override public void start() { - reportDir = new File(projectReactor.getRoot().getWorkDir(), "batch-report"); + reportDir = new File(moduleHierarchy.root().getWorkDir(), "batch-report"); writer = new ScannerReportWriter(reportDir); contextPublisher.init(writer); @@ -165,14 +168,13 @@ public class ReportPublisher implements Startable { String upload(File report) { LOG.debug("Upload report"); long startTime = System.currentTimeMillis(); - ProjectDefinition projectDefinition = projectReactor.getRoot(); PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, report); PostRequest post = new PostRequest("api/ce/submit") .setMediaType(MediaTypes.PROTOBUF) .setParam("organization", settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).orElse(null)) - .setParam("projectKey", projectDefinition.getKey()) - .setParam("projectName", projectDefinition.getOriginalName()) - .setParam("projectBranch", projectDefinition.getBranch()) + .setParam("projectKey", moduleHierarchy.root().key()) + .setParam("projectName", moduleHierarchy.root().getOriginalName()) + .setParam("projectBranch", moduleHierarchy.root().getBranch()) .setPart("report", filePart); WsResponse response; @@ -201,7 +203,7 @@ public class ReportPublisher implements Startable { HttpUrl httpUrl = HttpUrl.parse(publicUrl); Map<String, String> metadata = new LinkedHashMap<>(); - String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); + String effectiveKey = moduleHierarchy.root().getKeyWithBranch(); settings.get(CoreProperties.PROJECT_ORGANIZATION_PROPERTY).ifPresent(org -> metadata.put("organization", org)); metadata.put("projectKey", effectiveKey); metadata.put("serverUrl", publicUrl); @@ -230,7 +232,7 @@ public class ReportPublisher implements Startable { } private void dumpMetadata(Map<String, String> metadata) { - Path file = projectReactor.getRoot().getWorkDir().toPath().resolve(METADATA_DUMP_FILENAME); + Path file = moduleHierarchy.root().getWorkDir().toPath().resolve(METADATA_DUMP_FILENAME); try (Writer output = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { for (Map.Entry<String, String> entry : metadata.entrySet()) { output.write(entry.getKey()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java index c6c0015a88e..4c36c0991ad 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ContextPropertiesCache.java @@ -19,11 +19,12 @@ */ package org.sonar.scanner.repository; +import static com.google.common.base.Preconditions.checkArgument; + import java.util.HashMap; import java.util.Map; -import org.sonar.api.batch.ScannerSide; -import static com.google.common.base.Preconditions.checkArgument; +import org.sonar.api.batch.ScannerSide; @ScannerSide public class ContextPropertiesCache { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java index 6c9921c53a2..ead8f33b909 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ProjectRepositories.java @@ -19,30 +19,32 @@ */ package org.sonar.scanner.repository; -import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableTable; import com.google.common.collect.Table; import java.util.Date; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +@Immutable public class ProjectRepositories { - private final Table<String, String, String> settingsByModule; - private final Table<String, String, FileData> fileDataByModuleAndPath; + private final ImmutableTable<String, String, String> settingsByModule; + private final ImmutableTable<String, String, FileData> fileDataByModuleAndPath; private final Date lastAnalysisDate; private final boolean exists; public ProjectRepositories() { this.exists = false; - this.settingsByModule = HashBasedTable.create(); - this.fileDataByModuleAndPath = HashBasedTable.create(); + this.settingsByModule = new ImmutableTable.Builder<String, String, String>().build(); + this.fileDataByModuleAndPath = new ImmutableTable.Builder<String, String, FileData>().build(); this.lastAnalysisDate = null; } public ProjectRepositories(Table<String, String, String> settingsByModule, Table<String, String, FileData> fileDataByModuleAndPath, @Nullable Date lastAnalysisDate) { - this.settingsByModule = settingsByModule; - this.fileDataByModuleAndPath = fileDataByModuleAndPath; + this.settingsByModule = ImmutableTable.copyOf(settingsByModule); + this.fileDataByModuleAndPath = ImmutableTable.copyOf(fileDataByModuleAndPath); this.lastAnalysisDate = lastAnalysisDate; this.exists = true; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java index 4af9e6a6e74..8866237e54c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesRepository.java @@ -21,7 +21,10 @@ package org.sonar.scanner.repository.language; import java.util.ArrayList; import java.util.Collection; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.picocontainer.Startable; import org.sonar.api.resources.Languages; @@ -29,6 +32,7 @@ import org.sonar.api.resources.Languages; * Languages repository using {@link Languages} * @since 4.4 */ +@Immutable public class DefaultLanguagesRepository implements LanguagesRepository, Startable { private Languages languages; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java index 8b90c65f57e..775c895bfdb 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/Language.java @@ -22,6 +22,9 @@ package org.sonar.scanner.repository.language; import java.util.Arrays; import java.util.Collection; +import javax.annotation.concurrent.Immutable; + +@Immutable public final class Language { private final String key; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java index 626a3361f8f..d6f5046ea66 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/LanguagesRepository.java @@ -20,7 +20,10 @@ package org.sonar.scanner.repository.language; import java.util.Collection; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.ScannerSide; /** @@ -28,6 +31,7 @@ import org.sonar.api.batch.ScannerSide; * @since 4.4 */ @ScannerSide +@Immutable public interface LanguagesRepository { /** diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java index 013e22e352e..30d87568514 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ModuleQProfiles.java @@ -19,19 +19,24 @@ */ package org.sonar.scanner.rule; +import org.sonar.api.utils.DateUtils; + +import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; +import org.sonar.api.batch.ScannerSide; + +import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.utils.DateUtils; -import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; /** * Lists the Quality profiles enabled on the current module. */ @ScannerSide +@Immutable public class ModuleQProfiles { public static final String SONAR_PROFILE_PROP = "sonar.profile"; @@ -42,11 +47,11 @@ public class ModuleQProfiles { for (QualityProfile qProfile : profiles) { map.put(qProfile.getLanguage(), - new QProfile() + new QProfile.Builder() .setKey(qProfile.getKey()) .setName(qProfile.getName()) .setLanguage(qProfile.getLanguage()) - .setRulesUpdatedAt(DateUtils.parseDateTime(qProfile.getRulesUpdatedAt()))); + .setRulesUpdatedAt(DateUtils.parseDateTime(qProfile.getRulesUpdatedAt())).build()); } byLanguage = Collections.unmodifiableMap(map); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java index a38f942b929..9568368b992 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/QProfile.java @@ -22,49 +22,38 @@ package org.sonar.scanner.rule; import com.google.common.base.MoreObjects; import java.util.Date; +import javax.annotation.concurrent.Immutable; + +@Immutable public class QProfile { + private final String key; + private final String name; + private final String language; + private final Date rulesUpdatedAt; - private String key; - private String name; - private String language; - private Date rulesUpdatedAt; + public QProfile(String key, String name, String language, Date rulesUpdatedAt) { + this.key = key; + this.name = name; + this.language = language; + this.rulesUpdatedAt = rulesUpdatedAt; + } public String getKey() { return key; } - public QProfile setKey(String key) { - this.key = key; - return this; - } - public String getName() { return name; } - public QProfile setName(String name) { - this.name = name; - return this; - } - public String getLanguage() { return language; } - public QProfile setLanguage(String language) { - this.language = language; - return this; - } - public Date getRulesUpdatedAt() { return rulesUpdatedAt; } - public QProfile setRulesUpdatedAt(Date d) { - this.rulesUpdatedAt = d; - return this; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -92,4 +81,51 @@ public class QProfile { .add("rulesUpdatedAt", rulesUpdatedAt) .toString(); } + + public static class Builder { + private String key; + private String name; + private String language; + private Date rulesUpdatedAt; + + public String getKey() { + return key; + } + + public Builder setKey(String key) { + this.key = key; + return this; + } + + public String getName() { + return name; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public String getLanguage() { + return language; + } + + public Builder setLanguage(String language) { + this.language = language; + return this; + } + + public Date getRulesUpdatedAt() { + return rulesUpdatedAt; + } + + public Builder setRulesUpdatedAt(Date d) { + this.rulesUpdatedAt = d; + return this; + } + + public QProfile build() { + return new QProfile(key, name, language, rulesUpdatedAt); + } + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java index bd3d822bdec..2acf1e4e8f4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/RuleFinderCompatibility.java @@ -25,6 +25,8 @@ import java.util.Collections; import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.batch.rule.Rules; @@ -33,6 +35,7 @@ import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RuleQuery; +@Immutable public class RuleFinderCompatibility implements RuleFinder { private final Rules rules; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java index ae7f1de06ec..4cf6dcbcab8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java @@ -19,35 +19,68 @@ */ package org.sonar.scanner.scan; -import com.google.common.base.Preconditions; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import java.nio.file.Path; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; + import javax.annotation.CheckForNull; -import org.sonar.api.batch.bootstrap.ProjectDefinition; +import javax.annotation.concurrent.Immutable; + import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.scan.filesystem.PathResolver; +import com.google.common.collect.ImmutableMultimap; + +@Immutable public class DefaultInputModuleHierarchy implements InputModuleHierarchy { private final PathResolver pathResolver = new PathResolver(); - private DefaultInputModule root; - private final Map<DefaultInputModule, DefaultInputModule> parents = new HashMap<>(); - private final Multimap<DefaultInputModule, DefaultInputModule> children = HashMultimap.create(); + private final DefaultInputModule root; + private final Map<DefaultInputModule, DefaultInputModule> parents; + private final ImmutableMultimap<DefaultInputModule, DefaultInputModule> children; - public void setRoot(DefaultInputModule root) { + public DefaultInputModuleHierarchy(DefaultInputModule parent, DefaultInputModule child) { + this(Collections.singletonMap(child, parent)); + } + + public DefaultInputModuleHierarchy(DefaultInputModule root) { + this.children = new ImmutableMultimap.Builder<DefaultInputModule, DefaultInputModule>().build(); + this.parents = Collections.emptyMap(); this.root = root; } - public void index(DefaultInputModule child, DefaultInputModule parent) { - Preconditions.checkNotNull(child); - Preconditions.checkNotNull(parent); - parents.put(child, parent); - children.put(parent, child); + /** + * Map of child->parent. Neither the Keys or values can be null. + */ + public DefaultInputModuleHierarchy(Map<DefaultInputModule, DefaultInputModule> parents) { + ImmutableMultimap.Builder<DefaultInputModule, DefaultInputModule> childrenBuilder = new ImmutableMultimap.Builder<>(); + + for (Map.Entry<DefaultInputModule, DefaultInputModule> e : parents.entrySet()) { + childrenBuilder.put(e.getValue(), e.getKey()); + } + + this.children = childrenBuilder.build(); + this.parents = Collections.unmodifiableMap(new HashMap<>(parents)); + this.root = findRoot(parents); + } + + private static DefaultInputModule findRoot(Map<DefaultInputModule, DefaultInputModule> parents) { + DefaultInputModule r = null; + for (DefaultInputModule parent : parents.values()) { + if (!parents.containsKey(parent)) { + if (r != null && r != parent) { + throw new IllegalStateException(String.format("Found two modules without parent: '%s' and '%s'", r.key(), parent.key())); + } + r = parent; + } + } + if (r == null) { + throw new IllegalStateException("Found no root module"); + } + return r; } @Override @@ -78,11 +111,8 @@ public class DefaultInputModuleHierarchy implements InputModuleHierarchy { return null; } DefaultInputModule inputModule = (DefaultInputModule) module; - - ProjectDefinition parentDefinition = parent.definition(); - Path parentBaseDir = parentDefinition.getBaseDir().toPath(); - ProjectDefinition moduleDefinition = inputModule.definition(); - Path moduleBaseDir = moduleDefinition.getBaseDir().toPath(); + Path parentBaseDir = parent.getBaseDir().toPath(); + Path moduleBaseDir = inputModule.getBaseDir().toPath(); return pathResolver.relativePath(parentBaseDir, moduleBaseDir); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java deleted file mode 100644 index 6d5df79b123..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.scan; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.bootstrap.ProjectDefinition; - -/** - * Immutable copy of project reactor after all modifications have been applied (see {@link ImmutableProjectReactorProvider}). - */ -@ScannerSide -public class ImmutableProjectReactor { - - private ProjectDefinition root; - private Map<String, ProjectDefinition> byKey = new LinkedHashMap<>(); - - public ImmutableProjectReactor(ProjectDefinition root) { - if (root.getParent() != null) { - throw new IllegalArgumentException("Not a root project: " + root); - } - this.root = root; - collectProjects(root); - } - - public Collection<ProjectDefinition> getProjects() { - return byKey.values(); - } - - /** - * Populates list of projects from hierarchy. - */ - private void collectProjects(ProjectDefinition def) { - if (byKey.containsKey(def.getKeyWithBranch())) { - throw new IllegalStateException("Duplicate module key in reactor: " + def.getKeyWithBranch()); - } - byKey.put(def.getKeyWithBranch(), def); - for (ProjectDefinition child : def.getSubProjects()) { - collectProjects(child); - } - } - - public ProjectDefinition getRoot() { - return root; - } - - @CheckForNull - public ProjectDefinition getProjectDefinition(String keyWithBranch) { - return byKey.get(keyWithBranch); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java new file mode 100644 index 00000000000..aaa8cf5f54d --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.scanner.scan; + +import java.util.HashMap; +import java.util.Map; + +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.scanner.scan.filesystem.BatchIdGenerator; + +public class InputModuleHierarchyProvider extends ProviderAdapter { + + private DefaultInputModuleHierarchy hierarchy = null; + + public DefaultInputModuleHierarchy provide(ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator, + ProjectReactor projectReactor, BatchIdGenerator batchIdGenerator) { + if (hierarchy == null) { + // 1 Apply project builders + projectBuildersExecutor.execute(projectReactor); + + // 2 Validate final reactor + validator.validate(projectReactor); + + // 3 Create modules and the hierarchy + DefaultInputModule root = new DefaultInputModule(projectReactor.getRoot(), batchIdGenerator.get()); + Map<DefaultInputModule, DefaultInputModule> parents = createChildren(root, batchIdGenerator, new HashMap<>()); + if (parents.isEmpty()) { + hierarchy = new DefaultInputModuleHierarchy(root); + } else { + hierarchy = new DefaultInputModuleHierarchy(parents); + } + } + return hierarchy; + } + + private static Map<DefaultInputModule, DefaultInputModule> createChildren(DefaultInputModule parent, BatchIdGenerator batchIdGenerator, + Map<DefaultInputModule, DefaultInputModule> parents) { + for (ProjectDefinition def : parent.definition().getSubProjects()) { + DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get()); + parents.put(child, parent); + createChildren(child, batchIdGenerator, parents); + } + return parents; + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java index 5558d577ade..110baeb2287 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java @@ -20,9 +20,8 @@ package org.sonar.scanner.scan; import org.picocontainer.Startable; -import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.internal.DefaultInputModule; -import org.sonar.scanner.scan.filesystem.BatchIdGenerator; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.scanner.scan.filesystem.InputComponentStore; /** @@ -30,36 +29,27 @@ import org.sonar.scanner.scan.filesystem.InputComponentStore; * project definitions provided by the {@link ImmutableProjectReactor}. */ public class ModuleIndexer implements Startable { - private final ImmutableProjectReactor projectReactor; private final DefaultComponentTree componentTree; - private final DefaultInputModuleHierarchy moduleHierarchy; - private final BatchIdGenerator batchIdGenerator; + private final InputModuleHierarchy moduleHierarchy; private final InputComponentStore componentStore; - public ModuleIndexer(ImmutableProjectReactor projectReactor, DefaultComponentTree componentTree, - InputComponentStore componentStore, BatchIdGenerator batchIdGenerator, DefaultInputModuleHierarchy moduleHierarchy) { - this.projectReactor = projectReactor; + public ModuleIndexer(DefaultComponentTree componentTree, InputComponentStore componentStore, InputModuleHierarchy moduleHierarchy) { this.componentTree = componentTree; this.componentStore = componentStore; this.moduleHierarchy = moduleHierarchy; - this.batchIdGenerator = batchIdGenerator; } @Override public void start() { - DefaultInputModule root = new DefaultInputModule(projectReactor.getRoot(), batchIdGenerator.get()); - moduleHierarchy.setRoot(root); - componentStore.put(root); - createChildren(root); + DefaultInputModule root = moduleHierarchy.root(); + indexChildren(root); } - private void createChildren(DefaultInputModule parent) { - for (ProjectDefinition def : parent.definition().getSubProjects()) { - DefaultInputModule child = new DefaultInputModule(def, batchIdGenerator.get()); - moduleHierarchy.index(child, parent); - componentTree.index(child, parent); - componentStore.put(child); - createChildren(child); + private void indexChildren(DefaultInputModule parent) { + for (DefaultInputModule module : moduleHierarchy.children(parent)) { + componentTree.index(module, parent); + componentStore.put(module); + indexChildren(module); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java index 8305472e8aa..dbb3862d831 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java @@ -95,7 +95,7 @@ public class ModuleScanContainer extends ComponentContainer { add( module.definition(), // still injected by some plugins - new Project(module.definition()), + new Project(module), module, MutableModuleSettings.class, new ModuleSettingsProvider()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java index d17a9c0916f..25db68aad37 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java @@ -27,6 +27,7 @@ import java.util.Map; import org.picocontainer.injectors.ProviderAdapter; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.scanner.bootstrap.GlobalConfiguration; import org.sonar.scanner.report.AnalysisContextReportPublisher; import org.sonar.scanner.repository.ProjectRepositories; @@ -35,15 +36,15 @@ public class ModuleSettingsProvider extends ProviderAdapter { private ModuleSettings projectSettings; - public ModuleSettings provide(GlobalConfiguration globalSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectRepos, + public ModuleSettings provide(GlobalConfiguration globalSettings, DefaultInputModule module, ProjectRepositories projectRepos, AnalysisMode analysisMode, AnalysisContextReportPublisher contextReportPublisher) { if (projectSettings == null) { Map<String, String> settings = new LinkedHashMap<>(); settings.putAll(globalSettings.getProperties()); - settings.putAll(addServerSidePropertiesIfModuleExists(projectRepos, moduleDefinition)); - addScannerSideProperties(settings, moduleDefinition); - contextReportPublisher.dumpModuleSettings(moduleDefinition); + settings.putAll(addServerSidePropertiesIfModuleExists(projectRepos, module.definition())); + addScannerSideProperties(settings, module.definition()); + contextReportPublisher.dumpModuleSettings(module); projectSettings = new ModuleSettings(globalSettings.getDefinitions(), globalSettings.getEncryption(), analysisMode, settings); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java index bddf206cc59..d8af158590d 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java @@ -23,16 +23,17 @@ import java.io.IOException; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; import java.nio.file.Path; + import org.picocontainer.Startable; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.home.cache.DirectoryLock; import org.sonar.scanner.bootstrap.Slf4jLogger; public class ProjectLock implements Startable { private final DirectoryLock lock; - public ProjectLock(ProjectReactor projectReactor) { - Path directory = projectReactor.getRoot().getWorkDir().toPath(); + public ProjectLock(InputModuleHierarchy moduleHierarchy) { + Path directory = moduleHierarchy.root().getWorkDir().toPath(); try { if (!directory.toFile().exists()) { Files.createDirectories(directory); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java index f5f9d0886e7..e69eb31916e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java @@ -19,30 +19,28 @@ */ package org.sonar.scanner.scan; -import com.google.common.base.Joiner; import java.util.ArrayList; import java.util.List; + import javax.annotation.Nullable; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.config.Settings; import org.sonar.api.utils.MessageException; import org.sonar.core.component.ComponentKeys; import org.sonar.scanner.analysis.DefaultAnalysisMode; +import com.google.common.base.Joiner; + /** * This class aims at validating project reactor * @since 3.6 */ public class ProjectReactorValidator { - - private static final String SONAR_PHASE = "sonar.phase"; - private final Settings settings; private final DefaultAnalysisMode mode; - public ProjectReactorValidator(Settings settings, DefaultAnalysisMode mode) { - this.settings = settings; + public ProjectReactorValidator(DefaultAnalysisMode mode) { this.mode = mode; } @@ -50,7 +48,6 @@ public class ProjectReactorValidator { String branch = reactor.getRoot().getBranch(); List<String> validationMessages = new ArrayList<>(); - checkDeprecatedProperties(validationMessages); for (ProjectDefinition moduleDef : reactor.getProjects()) { if (mode.isIssues()) { @@ -81,12 +78,6 @@ public class ProjectReactorValidator { } } - private void checkDeprecatedProperties(List<String> validationMessages) { - if (settings.getString(SONAR_PHASE) != null) { - validationMessages.add(String.format("Property \"%s\" is deprecated. Please remove it from your configuration.", SONAR_PHASE)); - } - } - private static void validateBranch(List<String> validationMessages, @Nullable String branch) { if (StringUtils.isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) { validationMessages.add(String.format("\"%s\" is not a valid branch name. " diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index 9720666b113..458239cc526 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -19,7 +19,6 @@ */ package org.sonar.scanner.scan; -import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; @@ -42,6 +41,7 @@ import org.sonar.scanner.bootstrap.ExtensionMatcher; import org.sonar.scanner.bootstrap.ExtensionUtils; import org.sonar.scanner.bootstrap.MetricProvider; import org.sonar.scanner.cpd.CpdExecutor; +import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; import org.sonar.scanner.deprecated.test.TestPlanBuilder; import org.sonar.scanner.deprecated.test.TestableBuilder; @@ -86,12 +86,14 @@ import org.sonar.scanner.rule.DefaultRulesLoader; import org.sonar.scanner.rule.RulesLoader; import org.sonar.scanner.rule.RulesProvider; import org.sonar.scanner.scan.filesystem.BatchIdGenerator; -import org.sonar.scanner.scan.filesystem.InputComponentStore; +import org.sonar.scanner.scan.filesystem.InputComponentStoreProvider; import org.sonar.scanner.scan.measure.DefaultMetricFinder; import org.sonar.scanner.scan.measure.DeprecatedMetricFinder; import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.storage.Storages; +import com.google.common.annotations.VisibleForTesting; + public class ProjectScanContainer extends ComponentContainer { private static final Logger LOG = Loggers.get(ProjectScanContainer.class); @@ -106,10 +108,10 @@ public class ProjectScanContainer extends ComponentContainer { @Override protected void doBeforeStart() { addBatchComponents(); + addBatchExtensions(); ProjectLock lock = getComponentByType(ProjectLock.class); lock.tryLock(); getComponentByType(WorkDirectoryCleaner.class).execute(); - addBatchExtensions(); Settings settings = getComponentByType(Settings.class); if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) { add(PhasesSumUpTimeProfiler.class); @@ -126,7 +128,6 @@ public class ProjectScanContainer extends ComponentContainer { ProjectReactorBuilder.class, WorkDirectoryCleaner.class, new MutableProjectReactorProvider(), - new ImmutableProjectReactorProvider(), ProjectBuildersExecutor.class, ProjectLock.class, EventBus.class, @@ -145,9 +146,9 @@ public class ProjectScanContainer extends ComponentContainer { // file system ModuleIndexer.class, - InputComponentStore.class, + new InputComponentStoreProvider(), PathResolver.class, - DefaultInputModuleHierarchy.class, + new InputModuleHierarchyProvider(), DefaultComponentTree.class, BatchIdGenerator.class, @@ -197,6 +198,7 @@ public class ProjectScanContainer extends ComponentContainer { // Cpd CpdExecutor.class, + CpdSettings.class, SonarCpdBlockIndex.class, ScanTaskObservers.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java index 607b7bb0397..277b230fd88 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java @@ -24,15 +24,16 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Iterator; -import org.sonar.api.batch.bootstrap.ProjectReactor; + +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.core.util.FileUtils; import org.sonar.home.cache.DirectoryLock; public class WorkDirectoryCleaner { - private Path workDir; + private final Path workDir; - public WorkDirectoryCleaner(ProjectReactor projectReactor) { - workDir = projectReactor.getRoot().getWorkDir().toPath(); + public WorkDirectoryCleaner(InputModuleHierarchy moduleHierarchy) { + workDir = moduleHierarchy.root().getWorkDir().toPath(); } public void execute() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java index 087f8445ad3..119d8d9bc9f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java @@ -21,6 +21,9 @@ package org.sonar.scanner.scan.filesystem; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.fs.InputComponent; /** @@ -28,6 +31,7 @@ import org.sonar.api.batch.fs.InputComponent; * The IDs must be unique among all types of components and for all modules in the project. * The ID should never be 0, as it is sometimes used to indicate invalid components. */ +@ThreadSafe public class BatchIdGenerator implements Supplier<Integer> { private AtomicInteger nextBatchId = new AtomicInteger(1); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java index 0bebac55636..f84bec040cf 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java @@ -84,7 +84,7 @@ public class ExclusionFilters { if (inclusionPatterns.length > 0) { boolean matchInclusion = false; for (PathPattern pattern : inclusionPatterns) { - matchInclusion |= pattern.match(indexedFile); + matchInclusion |= pattern.match(indexedFile.absolutePath(), indexedFile.relativePath()); } if (!matchInclusion) { return false; @@ -92,7 +92,7 @@ public class ExclusionFilters { } if (exclusionPatterns.length > 0) { for (PathPattern pattern : exclusionPatterns) { - if (pattern.match(indexedFile)) { + if (pattern.match(indexedFile.absolutePath(), indexedFile.relativePath())) { return false; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java index dd8a32e91e5..3af3741e987 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java @@ -157,9 +157,10 @@ public class FileIndexer { DefaultInputFile inputFile = inputFileBuilder.create(realFile, type, fileSystem.encoding()); if (inputFile != null) { if (exclusionFilters.accept(inputFile, type) && accept(inputFile)) { + String parentRelativePath = getParentRelativePath(fileSystem, inputFile); synchronized (this) { fileSystem.add(inputFile); - indexParentDir(fileSystem, inputFile); + indexParentDir(fileSystem, inputFile, parentRelativePath); progress.markAsIndexed(inputFile); } LOG.debug("'{}' indexed {}with language '{}'", inputFile.relativePath(), type == Type.TEST ? "as test " : "", inputFile.language()); @@ -171,16 +172,19 @@ public class FileIndexer { return null; } - private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile) { + private static String getParentRelativePath(DefaultModuleFileSystem fileSystem, InputFile inputFile) { Path parentDir = inputFile.path().getParent(); String relativePath = new PathResolver().relativePath(fileSystem.baseDirPath(), parentDir); if (relativePath == null) { throw new IllegalStateException("Failed to compute relative path of file: " + inputFile); } + return relativePath; + } - DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), relativePath); + private void indexParentDir(DefaultModuleFileSystem fileSystem, InputFile inputFile, String parentRelativePath) { + DefaultInputDir inputDir = (DefaultInputDir) componentStore.getDir(module.key(), parentRelativePath); if (inputDir == null) { - inputDir = new DefaultInputDir(fileSystem.moduleKey(), relativePath, batchIdGenerator.get()); + inputDir = new DefaultInputDir(fileSystem.moduleKey(), parentRelativePath, batchIdGenerator.get()); inputDir.setModuleBaseDir(fileSystem.baseDirPath()); fileSystem.add(inputDir); componentTree.index(inputDir, module); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java index ff92762a405..6726cba970b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java @@ -58,14 +58,17 @@ public class InputComponentStore { private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create(); private final Map<String, InputDir> globalInputDirCache = new HashMap<>(); private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create(); + // indexed by key with branch private final Map<String, InputModule> inputModuleCache = new HashMap<>(); private final Map<String, InputComponent> inputComponents = new HashMap<>(); private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create(); private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create(); - private InputModule root; + private final InputModule root; - public InputComponentStore(PathResolver pathResolver) { + public InputComponentStore(PathResolver pathResolver, DefaultInputModule root) { this.pathResolver = pathResolver; + this.root = root; + this.put(root); } public Collection<InputComponent> all() { @@ -90,7 +93,6 @@ public class InputComponentStore { return inputComponents.get(key); } - @CheckForNull public InputModule root() { return root; } @@ -157,7 +159,7 @@ public class InputComponentStore { } private Path getProjectBaseDir() { - return ((DefaultInputModule) root).definition().getBaseDir().toPath(); + return ((DefaultInputModule) root).getBaseDir().toPath(); } @CheckForNull @@ -181,22 +183,18 @@ public class InputComponentStore { } @CheckForNull - public InputModule getModule(String moduleKey) { - return inputModuleCache.get(moduleKey); + public InputModule getModule(String moduleKeyWithBranch) { + return inputModuleCache.get(moduleKeyWithBranch); } public void put(DefaultInputModule inputModule) { String key = inputModule.key(); + String keyWithBranch = inputModule.getKeyWithBranch(); + Preconditions.checkNotNull(inputModule); Preconditions.checkState(!inputComponents.containsKey(key), "Module '%s' already indexed", key); - Preconditions.checkState(!inputModuleCache.containsKey(key), "Module '%s' already indexed", key); + Preconditions.checkState(!inputModuleCache.containsKey(keyWithBranch), "Module '%s' already indexed", keyWithBranch); inputComponents.put(key, inputModule); - inputModuleCache.put(key, inputModule); - if (inputModule.definition().getParent() == null) { - if (root != null) { - throw new IllegalStateException("Root module already indexed: '" + root.key() + "', '" + key + "'"); - } - root = inputModule; - } + inputModuleCache.put(keyWithBranch, inputModule); } public Iterable<InputFile> getFilesByName(String filename) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactorProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStoreProvider.java index d3111db3a46..65e78e623e6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactorProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStoreProvider.java @@ -17,25 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.scanner.scan; +package org.sonar.scanner.scan.filesystem; import org.picocontainer.injectors.ProviderAdapter; -import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.scan.filesystem.PathResolver; -public class ImmutableProjectReactorProvider extends ProviderAdapter { +public class InputComponentStoreProvider extends ProviderAdapter { + private InputComponentStore store; - private ImmutableProjectReactor singleton; - - public ImmutableProjectReactor provide(ProjectReactor reactor, ProjectBuildersExecutor projectBuildersExecutor, ProjectReactorValidator validator) { - if (singleton == null) { - // 1 Apply project builders - projectBuildersExecutor.execute(reactor); - - // 2 Validate final reactor - validator.validate(reactor); - - singleton = new ImmutableProjectReactor(reactor.getRoot()); + public InputComponentStore provide(PathResolver pathResolver, InputModuleHierarchy hierarchy) { + if (store == null) { + store = new InputComponentStore(pathResolver, hierarchy.root()); } - return singleton; + return store; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java index c44e24f4942..fe8d125a5e9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java @@ -60,14 +60,13 @@ public class InputFileBuilder { LOG.warn("File '{}' is ignored. It is not located in module basedir '{}'.", file.toAbsolutePath(), moduleBaseDir); return null; } - DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, idGenerator.get()); - String language = langDetection.language(indexedFile); + String language = langDetection.language(file.toAbsolutePath().normalize().toString(), relativePath); if (language == null && langDetection.forcedLanguage() != null) { LOG.warn("File '{}' is ignored because it doesn't belong to the forced language '{}'", file.toAbsolutePath(), langDetection.forcedLanguage()); return null; } - indexedFile.setLanguage(language); + DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, idGenerator.get()); DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(f, defaultEncoding)); if (language != null) { inputFile.setPublish(true); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java index 828a8403655..8267e4509c9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java @@ -19,29 +19,34 @@ */ package org.sonar.scanner.scan.filesystem; -import com.google.common.base.Joiner; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + import javax.annotation.CheckForNull; +import javax.annotation.concurrent.ThreadSafe; + import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.batch.ScannerSide; -import org.sonar.api.batch.fs.internal.DefaultIndexedFile; import org.sonar.api.batch.fs.internal.PathPattern; import org.sonar.api.config.Configuration; import org.sonar.api.utils.MessageException; import org.sonar.scanner.repository.language.Language; import org.sonar.scanner.repository.language.LanguagesRepository; +import com.google.common.base.Joiner; + /** * Detect language of a source file based on its suffix and configured patterns. */ @ScannerSide +@ThreadSafe public class LanguageDetection { private static final Logger LOG = LoggerFactory.getLogger(LanguageDetection.class); @@ -49,16 +54,17 @@ public class LanguageDetection { /** * Lower-case extension -> languages */ - private final Map<String, PathPattern[]> patternsByLanguage = new LinkedHashMap<>(); - private final List<String> languagesToConsider = new ArrayList<>(); + private final Map<String, PathPattern[]> patternsByLanguage; + private final List<String> languagesToConsider; private final String forcedLanguage; public LanguageDetection(Configuration settings, LanguagesRepository languages) { + Map<String, PathPattern[]> patternsByLanguageBuilder = new LinkedHashMap<>(); for (Language language : languages.all()) { String[] filePatterns = settings.getStringArray(getFileLangPatternPropKey(language.key())); PathPattern[] pathPatterns = PathPattern.create(filePatterns); if (pathPatterns.length > 0) { - patternsByLanguage.put(language.key(), pathPatterns); + patternsByLanguageBuilder.put(language.key(), pathPatterns); } else { // If no custom language pattern is defined then fallback to suffixes declared by language String[] patterns = language.fileSuffixes().toArray(new String[language.fileSuffixes().size()]); @@ -68,22 +74,24 @@ public class LanguageDetection { patterns[i] = new StringBuilder().append("**/*.").append(extension).toString(); } PathPattern[] defaultLanguagePatterns = PathPattern.create(patterns); - patternsByLanguage.put(language.key(), defaultLanguagePatterns); - LOG.debug("Declared extensions of language {} were converted to {}", language, getDetails(language.key())); + patternsByLanguageBuilder.put(language.key(), defaultLanguagePatterns); + LOG.debug("Declared extensions of language {} were converted to {}", language, getDetails(language.key(), defaultLanguagePatterns)); } } forcedLanguage = StringUtils.defaultIfBlank(settings.get(CoreProperties.PROJECT_LANGUAGE_PROPERTY).orElse(null), null); // First try with lang patterns if (forcedLanguage != null) { - if (!patternsByLanguage.containsKey(forcedLanguage)) { + if (!patternsByLanguageBuilder.containsKey(forcedLanguage)) { throw MessageException.of("You must install a plugin that supports the language '" + forcedLanguage + "'"); } LOG.info("Language is forced to {}", forcedLanguage); - languagesToConsider.add(forcedLanguage); + languagesToConsider = Collections.singletonList(forcedLanguage); } else { - languagesToConsider.addAll(patternsByLanguage.keySet()); + languagesToConsider = Collections.unmodifiableList(new ArrayList<>(patternsByLanguageBuilder.keySet())); } + + patternsByLanguage = Collections.unmodifiableMap(patternsByLanguageBuilder); } public String forcedLanguage() { @@ -95,16 +103,16 @@ public class LanguageDetection { } @CheckForNull - String language(DefaultIndexedFile inputFile) { + String language(String absolutePath, String relativePath) { String detectedLanguage = null; for (String languageKey : languagesToConsider) { - if (isCandidateForLanguage(inputFile, languageKey)) { + if (isCandidateForLanguage(absolutePath, relativePath, languageKey)) { if (detectedLanguage == null) { detectedLanguage = languageKey; } else { // Language was already forced by another pattern throw MessageException.of(MessageFormat.format("Language of file ''{0}'' can not be decided as the file matches patterns of both {1} and {2}", - inputFile.relativePath(), getDetails(detectedLanguage), getDetails(languageKey))); + relativePath, getDetails(detectedLanguage), getDetails(languageKey))); } } } @@ -120,11 +128,11 @@ public class LanguageDetection { return null; } - private boolean isCandidateForLanguage(DefaultIndexedFile inputFile, String languageKey) { + private boolean isCandidateForLanguage(String absolutePath, String relativePath, String languageKey) { PathPattern[] patterns = patternsByLanguage.get(languageKey); if (patterns != null) { for (PathPattern pathPattern : patterns) { - if (pathPattern.match(inputFile, false)) { + if (pathPattern.match(absolutePath, relativePath, false)) { return true; } } @@ -137,7 +145,11 @@ public class LanguageDetection { } private String getDetails(String detectedLanguage) { - return getFileLangPatternPropKey(detectedLanguage) + " : " + Joiner.on(",").join(patternsByLanguage.get(detectedLanguage)); + return getDetails(detectedLanguage, patternsByLanguage.get(detectedLanguage)); + } + + private static String getDetails(String detectedLanguage, PathPattern[] patterns) { + return getFileLangPatternPropKey(detectedLanguage) + " : " + Joiner.on(",").join(patterns); } static String sanitizeExtension(String suffix) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java index 2c5c9e54e8d..f13b39b5325 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java @@ -22,6 +22,7 @@ package org.sonar.scanner.scan.filesystem; import java.io.File; import java.util.ArrayList; import java.util.List; + import org.apache.commons.io.FileUtils; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.bootstrap.ProjectDefinition; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java index 851332f663d..ac11c11d3b8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java @@ -19,11 +19,14 @@ */ package org.sonar.scanner.scan.filesystem; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; import org.sonar.scanner.repository.FileData; import org.sonar.scanner.repository.ProjectRepositories; +@Immutable class StatusDetection { private final ProjectRepositories projectSettings; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java index 00d484ef598..c6655b7d570 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java @@ -22,21 +22,28 @@ package org.sonar.scanner.scan.measure; import com.google.common.collect.Lists; import java.io.Serializable; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.scanner.repository.MetricsRepository; +@ThreadSafe public class DefaultMetricFinder implements MetricFinder { - private Map<String, Metric<Serializable>> metricsByKey = new LinkedHashMap<>(); + private Map<String, Metric<Serializable>> metricsByKey; public DefaultMetricFinder(MetricsRepository metricsRepository) { + Map<String, Metric<Serializable>> metrics = new LinkedHashMap<>(); for (org.sonar.api.measures.Metric metric : metricsRepository.metrics()) { - metricsByKey.put(metric.key(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), metric.getType()).create()); + metrics.put(metric.key(), new org.sonar.api.measures.Metric.Builder(metric.key(), metric.key(), metric.getType()).create()); } + metricsByKey = Collections.unmodifiableMap(metrics); } @Override diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java index eaaf9b00b0e..46531e99c40 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java @@ -20,6 +20,8 @@ package org.sonar.scanner.scan.report; import javax.annotation.CheckForNull; +import javax.annotation.concurrent.Immutable; + import org.apache.commons.lang.StringEscapeUtils; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.rule.Rule; @@ -27,6 +29,7 @@ import org.sonar.api.batch.rule.Rules; import org.sonar.api.rule.RuleKey; @ScannerSide +@Immutable public class RuleNameProvider { private Rules rules; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java index 68c75b647a6..73f29557bc0 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmConfiguration.java @@ -19,9 +19,9 @@ */ package org.sonar.scanner.scm; -import com.google.common.base.Joiner; import java.util.LinkedHashMap; import java.util.Map; + import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; import org.sonar.api.CoreProperties; @@ -31,11 +31,13 @@ import org.sonar.api.PropertyType; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.ScannerSide; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.scanner.scan.ImmutableProjectReactor; + +import com.google.common.base.Joiner; @Properties({ @Property( @@ -56,15 +58,15 @@ public final class ScmConfiguration implements Startable { public static final String FORCE_RELOAD_KEY = "sonar.scm.forceReloadAll"; - private final ImmutableProjectReactor projectReactor; private final Configuration settings; private final Map<String, ScmProvider> providerPerKey = new LinkedHashMap<>(); private final AnalysisMode analysisMode; + private final InputModuleHierarchy moduleHierarchy; private ScmProvider provider; - public ScmConfiguration(ImmutableProjectReactor projectReactor, AnalysisMode analysisMode, Configuration settings, ScmProvider... providers) { - this.projectReactor = projectReactor; + public ScmConfiguration(InputModuleHierarchy moduleHierarchy, AnalysisMode analysisMode, Configuration settings, ScmProvider... providers) { + this.moduleHierarchy = moduleHierarchy; this.analysisMode = analysisMode; this.settings = settings; for (ScmProvider scmProvider : providers) { @@ -72,8 +74,8 @@ public final class ScmConfiguration implements Startable { } } - public ScmConfiguration(ImmutableProjectReactor projectReactor, AnalysisMode analysisMode, Configuration settings) { - this(projectReactor, analysisMode, settings, new ScmProvider[0]); + public ScmConfiguration(InputModuleHierarchy moduleHierarchy, AnalysisMode analysisMode, Configuration settings) { + this(moduleHierarchy, analysisMode, settings, new ScmProvider[0]); } @Override @@ -121,7 +123,7 @@ public final class ScmConfiguration implements Startable { private void autodetection() { for (ScmProvider installedProvider : providerPerKey.values()) { - if (installedProvider.supports(projectReactor.getRoot().getBaseDir())) { + if (installedProvider.supports(moduleHierarchy.root().getBaseDir())) { if (this.provider == null) { this.provider = installedProvider; } else { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java index 9467d96829a..1889bbaab94 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java @@ -20,6 +20,9 @@ package org.sonar.scanner.sensor; import java.io.Serializable; + +import javax.annotation.concurrent.ThreadSafe; + import org.sonar.api.SonarRuntime; import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.FileSystem; @@ -50,6 +53,7 @@ import org.sonar.scanner.sensor.noop.NoOpNewCpdTokens; import org.sonar.scanner.sensor.noop.NoOpNewHighlighting; import org.sonar.scanner.sensor.noop.NoOpNewSymbolTable; +@ThreadSafe public class DefaultSensorContext implements SensorContext { private static final NoOpNewHighlighting NO_OP_NEW_HIGHLIGHTING = new NoOpNewHighlighting(); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index ac966c2a855..c6fc08c8ce4 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -19,52 +19,6 @@ */ package org.sonar.scanner.sensor; -import com.google.common.annotations.VisibleForTesting; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.TextRange; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.measure.Metric; -import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; -import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; -import org.sonar.api.batch.sensor.error.AnalysisError; -import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; -import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.batch.sensor.issue.Issue; -import org.sonar.api.batch.sensor.measure.Measure; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; -import org.sonar.api.config.Configuration; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.metric.ScannerMetrics; -import org.sonar.duplications.block.Block; -import org.sonar.duplications.internal.pmd.PmdBlockChunker; -import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; -import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.issue.ModuleIssues; -import org.sonar.scanner.protocol.output.FileStructure; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.report.ReportPublisher; -import org.sonar.scanner.report.ScannerReportUtils; -import org.sonar.scanner.repository.ContextPropertiesCache; -import org.sonar.scanner.scan.measure.MeasureCache; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; - import static java.util.stream.Collectors.toList; import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE; import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES_KEY; @@ -112,6 +66,54 @@ import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.measure.Metric; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; +import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; +import org.sonar.api.batch.sensor.error.AnalysisError; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.measure.Measure; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; +import org.sonar.api.config.Configuration; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.metric.ScannerMetrics; +import org.sonar.duplications.block.Block; +import org.sonar.duplications.internal.pmd.PmdBlockChunker; +import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; +import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; +import org.sonar.scanner.issue.ModuleIssues; +import org.sonar.scanner.protocol.output.FileStructure; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.report.ReportPublisher; +import org.sonar.scanner.report.ScannerReportUtils; +import org.sonar.scanner.repository.ContextPropertiesCache; +import org.sonar.scanner.scan.measure.MeasureCache; +import org.sonar.scanner.sensor.coverage.CoverageExclusions; + +import com.google.common.annotations.VisibleForTesting; + public class DefaultSensorStorage implements SensorStorage { private static final Logger LOG = Loggers.get(DefaultSensorStorage.class); @@ -151,12 +153,10 @@ public class DefaultSensorStorage implements SensorStorage { private final Map<Metric<?>, Metric<?>> deprecatedCoverageMetricMapping = new HashMap<>(); private final Set<Metric<?>> coverageMetrics = new HashSet<>(); private final Set<Metric<?>> byLineMetrics = new HashSet<>(); - private Set<String> alreadyLogged = new HashSet<>(); + private final Set<String> alreadyLogged = new HashSet<>(); - public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, - Configuration settings, - CoverageExclusions coverageExclusions, ReportPublisher reportPublisher, - MeasureCache measureCache, SonarCpdBlockIndex index, + public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Configuration settings, CoverageExclusions coverageExclusions, + ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index, ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics) { this.metricFinder = metricFinder; this.moduleIssues = moduleIssues; @@ -210,10 +210,12 @@ public class DefaultSensorStorage implements SensorStorage { saveMeasure(newMeasure.inputComponent(), (DefaultMeasure<?>) newMeasure); } + /** + * Thread safe + */ private void logOnce(String metricKey, String msg, Object... params) { - if (!alreadyLogged.contains(metricKey)) { + if (alreadyLogged.add(metricKey)) { LOG.warn(msg, params); - alreadyLogged.add(metricKey); } } @@ -313,11 +315,11 @@ public class DefaultSensorStorage implements SensorStorage { } } - public boolean isDeprecatedMetric(String metricKey) { + public static boolean isDeprecatedMetric(String metricKey) { return DEPRECATED_METRICS_KEYS.contains(metricKey); } - public boolean isPlatformMetric(String metricKey) { + public static boolean isPlatformMetric(String metricKey) { return PLATFORM_METRICS_KEYS.contains(metricKey); } @@ -325,7 +327,7 @@ public class DefaultSensorStorage implements SensorStorage { return this.byLineMetrics.contains(metric); } - public void validateCoverageMeasure(String value, InputFile inputFile) { + public static void validateCoverageMeasure(String value, InputFile inputFile) { Map<Integer, Integer> m = KeyValueFormat.parseIntInt(value); validatePositiveLine(m, inputFile.absolutePath()); validateMaxLine(m, inputFile); @@ -349,6 +351,9 @@ public class DefaultSensorStorage implements SensorStorage { } } + /** + * Thread safe assuming that each issues for each file are only written once. + */ @Override public void store(Issue issue) { if (issue.primaryLocation().inputComponent() instanceof DefaultInputFile) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java index 490f067d764..5af99184414 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java @@ -19,11 +19,11 @@ */ package org.sonar.scanner.sensor.coverage; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; import java.util.Collection; import java.util.Iterator; + +import javax.annotation.concurrent.Immutable; + import org.picocontainer.Startable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,20 +32,26 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Configuration; import org.sonar.api.utils.WildcardPattern; -public class CoverageExclusions implements Startable { +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +@Immutable +public class CoverageExclusions implements Startable { private static final Logger LOG = LoggerFactory.getLogger(CoverageExclusions.class); - private final Configuration settings; private Collection<WildcardPattern> exclusionPatterns; public CoverageExclusions(Configuration settings) { - this.settings = settings; + Builder<WildcardPattern> builder = ImmutableList.builder(); + for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) { + builder.add(WildcardPattern.create(pattern)); + } + exclusionPatterns = builder.build(); } @Override public void start() { - initPatterns(); + log("Excluded sources for coverage: ", exclusionPatterns); } @Override @@ -62,16 +68,6 @@ public class CoverageExclusions implements Startable { return found; } - @VisibleForTesting - final void initPatterns() { - Builder<WildcardPattern> builder = ImmutableList.builder(); - for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) { - builder.add(WildcardPattern.create(pattern)); - } - exclusionPatterns = builder.build(); - log("Excluded sources for coverage: ", exclusionPatterns); - } - private static void log(String title, Collection<WildcardPattern> patterns) { if (!patterns.isEmpty()) { LOG.info(title); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java index 8e2cb47094e..a5128b5baf3 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java @@ -19,12 +19,12 @@ */ package org.sonar.scanner.storage; -import com.google.common.collect.Sets; import com.persistit.Exchange; import com.persistit.Key; import com.persistit.KeyFilter; import com.persistit.exception.PersistitException; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.CheckForNull; @@ -242,7 +242,7 @@ public class Storage<V> { @SuppressWarnings("rawtypes") public Set keySet(Object key) { try { - Set<Object> keys = Sets.newLinkedHashSet(); + Set<Object> keys = new LinkedHashSet<>(); exchange.clear(); Exchange iteratorExchange = new Exchange(exchange); iteratorExchange.append(key); @@ -259,7 +259,7 @@ public class Storage<V> { @SuppressWarnings("rawtypes") public Set keySet(Object firstKey, Object secondKey) { try { - Set<Object> keys = Sets.newLinkedHashSet(); + Set<Object> keys = new LinkedHashSet<>(); exchange.clear(); Exchange iteratorExchange = new Exchange(exchange); iteratorExchange.append(firstKey); @@ -281,7 +281,7 @@ public class Storage<V> { */ public Set<Object> keySet() { try { - Set<Object> keys = Sets.newLinkedHashSet(); + Set<Object> keys = new LinkedHashSet<>(); exchange.clear(); Exchange iteratorExchange = new Exchange(exchange); iteratorExchange.append(Key.BEFORE); |