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/java/org/sonar/scanner/scan | |
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/java/org/sonar/scanner/scan')
21 files changed, 232 insertions, 197 deletions
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; |