aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2017-07-10 10:38:27 +0200
committerDuarte Meneses <duarte.meneses@sonarsource.com>2017-07-11 08:51:38 +0200
commit6f107dcbe90b0fea564e1ecaa96643bfc539329a (patch)
tree45478253f6006a0f1381fb506f92852ad2f5c5df /sonar-scanner-engine/src/main/java/org/sonar/scanner/scan
parentf6a0f6bb86f7d244bec0b6781020e2ea066124c6 (diff)
downloadsonarqube-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')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java66
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactor.java71
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/InputModuleHierarchyProvider.java65
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleIndexer.java30
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleSettingsProvider.java9
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectLock.java7
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java19
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java14
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/WorkDirectoryCleaner.java9
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/BatchIdGenerator.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ExclusionFilters.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/FileIndexer.java12
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java26
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStoreProvider.java (renamed from sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ImmutableProjectReactorProvider.java)24
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java5
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/LanguageDetection.java44
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/ModuleFileSystemInitializer.java1
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/StatusDetection.java3
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/DefaultMetricFinder.java11
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/report/RuleNameProvider.java3
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;