summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-06-10 00:41:17 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2014-06-10 12:36:11 +0200
commit9decc5bd48352c1fb9fceca9de8b7eca654fd0bf (patch)
tree0e95b2362e21f63347679e33ebefe8186ea58739
parentda92c49827337bd34fbf077d5d74c9fbfb8ec287 (diff)
downloadsonarqube-9decc5bd48352c1fb9fceca9de8b7eca654fd0bf.tar.gz
sonarqube-9decc5bd48352c1fb9fceca9de8b7eca654fd0bf.zip
SONAR-5389 Initial version of the new sensor mode
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java7
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java18
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java21
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultPluginsReferential.java (renamed from sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginDownloader.java)21
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginsReferential.java43
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java4
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java116
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java211
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java229
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java16
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/bootstrap/PluginDownloaderTest.java15
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java168
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java5
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleFinder.java8
15 files changed, 836 insertions, 48 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index eb8468ba5b6..0b22c904e59 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -120,13 +120,6 @@ import java.util.List;
@Properties({
@Property(
- key = CoreProperties.TASK,
- name = "Task to be executed",
- defaultValue = CoreProperties.SCAN_TASK,
- module = false,
- project = false,
- global = false),
- @Property(
key = CoreProperties.SERVER_BASE_URL,
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
index 2e2b09da23c..aea375f3c0d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
@@ -36,7 +36,11 @@ import org.sonar.core.plugins.RemotePlugin;
import java.io.File;
import java.text.MessageFormat;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
@@ -46,7 +50,7 @@ public class BatchPluginRepository implements PluginRepository {
private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class);
private static final String CORE_PLUGIN = "core";
- private PluginDownloader pluginDownloader;
+ private PluginsReferential pluginsReferential;
private Map<String, Plugin> pluginsByKey;
private Map<String, PluginMetadata> metadataByKey;
private Settings settings;
@@ -54,9 +58,9 @@ public class BatchPluginRepository implements PluginRepository {
private final AnalysisMode analysisMode;
private final BatchPluginJarInstaller pluginInstaller;
- public BatchPluginRepository(PluginDownloader pluginDownloader, Settings settings, AnalysisMode analysisMode,
- BatchPluginJarInstaller pluginInstaller) {
- this.pluginDownloader = pluginDownloader;
+ public BatchPluginRepository(PluginsReferential pluginsReferential, Settings settings, AnalysisMode analysisMode,
+ BatchPluginJarInstaller pluginInstaller) {
+ this.pluginsReferential = pluginsReferential;
this.settings = settings;
this.analysisMode = analysisMode;
this.pluginInstaller = pluginInstaller;
@@ -64,7 +68,7 @@ public class BatchPluginRepository implements PluginRepository {
public void start() {
LOG.info("Install plugins");
- doStart(pluginDownloader.downloadPluginIndex());
+ doStart(pluginsReferential.pluginList());
}
void doStart(List<RemotePlugin> remotePlugins) {
@@ -72,7 +76,7 @@ public class BatchPluginRepository implements PluginRepository {
metadataByKey = Maps.newHashMap();
for (RemotePlugin remote : remotePlugins) {
if (filter.accepts(remote.getKey())) {
- File pluginFile = pluginDownloader.downloadPlugin(remote);
+ File pluginFile = pluginsReferential.pluginFile(remote);
PluginMetadata metadata = pluginInstaller.installToCache(pluginFile, remote.isCore());
if (StringUtils.isBlank(metadata.getBasePlugin()) || filter.accepts(metadata.getBasePlugin())) {
metadataByKey.put(metadata.getKey(), metadata);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
index feffc2f6ede..992ccf77819 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
@@ -20,10 +20,13 @@
package org.sonar.batch.bootstrap;
import org.apache.commons.configuration.PropertiesConfiguration;
+import org.sonar.api.CoreProperties;
import org.sonar.api.Plugin;
import org.sonar.api.config.EmailSettings;
+import org.sonar.api.measures.MetricFinder;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.rules.RuleFinder;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.HttpDownloader;
import org.sonar.api.utils.System2;
@@ -63,9 +66,11 @@ import java.util.Map;
public class BootstrapContainer extends ComponentContainer {
private final Map<String, String> bootstrapProperties;
+ private final boolean sensorMode;
private BootstrapContainer(Map<String, String> bootstrapProperties) {
super();
+ this.sensorMode = CoreProperties.ANALYSIS_MODE_SENSOR.equals(bootstrapProperties.get(CoreProperties.ANALYSIS_MODE));
this.bootstrapProperties = bootstrapProperties;
}
@@ -78,7 +83,9 @@ public class BootstrapContainer extends ComponentContainer {
@Override
protected void doBeforeStart() {
addBootstrapComponents();
- addDatabaseComponents();
+ if (!sensorMode) {
+ addDatabaseComponents();
+ }
addCoreComponents();
}
@@ -87,7 +94,6 @@ public class BootstrapContainer extends ComponentContainer {
new PropertiesConfiguration(),
new BootstrapProperties(bootstrapProperties),
AnalysisMode.class,
- PluginDownloader.class,
BatchPluginRepository.class,
BatchPluginJarInstaller.class,
BatchSettings.class,
@@ -105,6 +111,15 @@ public class BootstrapContainer extends ComponentContainer {
if (getComponentByType(SettingsReferential.class) == null) {
add(DefaultSettingsReferential.class);
}
+ if (getComponentByType(PluginsReferential.class) == null) {
+ add(DefaultPluginsReferential.class);
+ }
+ if (getComponentByType(RuleFinder.class) == null) {
+ add(CacheRuleFinder.class);
+ }
+ if (getComponentByType(MetricFinder.class) == null) {
+ add(CacheMetricFinder.class);
+ }
}
private void addDatabaseComponents() {
@@ -135,8 +150,6 @@ public class BootstrapContainer extends ComponentContainer {
MeasuresDao.class,
RulesDao.class,
ProfilesDao.class,
- CacheRuleFinder.class,
- CacheMetricFinder.class,
HibernateUserFinder.class,
SemaphoreUpdater.class,
SemaphoresImpl.class,
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginDownloader.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultPluginsReferential.java
index 0af5f2a4e96..93ae4e97c34 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginDownloader.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultPluginsReferential.java
@@ -24,8 +24,6 @@ import org.apache.commons.lang.CharUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.sonar.api.BatchComponent;
-import org.sonar.api.utils.SonarException;
import org.sonar.core.plugins.RemotePlugin;
import org.sonar.core.plugins.RemotePluginFile;
import org.sonar.home.cache.FileCache;
@@ -34,19 +32,23 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
-public class PluginDownloader implements BatchComponent {
+/**
+ * A {@link PluginsReferential} implementation that put downloaded plugins in a FS cache.
+ */
+public class DefaultPluginsReferential implements PluginsReferential {
- private static final Logger LOG = LoggerFactory.getLogger(PluginDownloader.class);
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginsReferential.class);
private ServerClient server;
private FileCache fileCache;
- public PluginDownloader(FileCache fileCache, ServerClient server) {
+ public DefaultPluginsReferential(FileCache fileCache, ServerClient server) {
this.server = server;
this.fileCache = fileCache;
}
- public File downloadPlugin(final RemotePlugin remote) {
+ @Override
+ public File pluginFile(final RemotePlugin remote) {
try {
final RemotePluginFile file = remote.file();
File cachedFile = fileCache.get(file.getFilename(), file.getHash(), new FileCache.Downloader() {
@@ -63,11 +65,12 @@ public class PluginDownloader implements BatchComponent {
return cachedFile;
} catch (Exception e) {
- throw new SonarException("Fail to download plugin: " + remote.getKey(), e);
+ throw new IllegalStateException("Fail to download plugin: " + remote.getKey(), e);
}
}
- public List<RemotePlugin> downloadPluginIndex() {
+ @Override
+ public List<RemotePlugin> pluginList() {
String url = "/deploy/plugins/index.txt";
try {
LOG.debug("Download index of plugins");
@@ -80,7 +83,7 @@ public class PluginDownloader implements BatchComponent {
return remoteLocations;
} catch (Exception e) {
- throw new SonarException("Fail to download plugins index: " + url, e);
+ throw new IllegalStateException("Fail to download plugins index: " + url, e);
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginsReferential.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginsReferential.java
new file mode 100644
index 00000000000..daf38ae7abf
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginsReferential.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.bootstrap;
+
+import org.sonar.core.plugins.RemotePlugin;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Plugin referential.
+ * @since 4.4
+ */
+public interface PluginsReferential {
+
+ /**
+ * Return list of plugins to be installed
+ */
+ List<RemotePlugin> pluginList();
+
+ /**
+ * Return location of a given plugin on the local FS.
+ */
+ File pluginFile(RemotePlugin remote);
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
index 4bdda269f46..86122e7dec4 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
@@ -19,8 +19,8 @@
*/
package org.sonar.batch.bootstrap;
+import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.task.Task;
@@ -94,7 +94,7 @@ public class TaskContainer extends ComponentContainer {
@Override
public void doAfterStart() {
// default value is declared in CorePlugin
- String taskKey = getComponentByType(Settings.class).getString(CoreProperties.TASK);
+ String taskKey = StringUtils.defaultIfEmpty(taskProperties.get(CoreProperties.TASK), CoreProperties.SCAN_TASK);
TaskDefinition def = getComponentByType(Tasks.class).definition(taskKey);
if (def == null) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java
new file mode 100644
index 00000000000..87d1348ae3b
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java
@@ -0,0 +1,116 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.phases;
+
+import com.google.common.collect.Lists;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.events.EventBus;
+import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.filesystem.FileSystemLogger;
+import org.sonar.batch.scan.maven.MavenPluginsConfigurator;
+
+import java.util.Collection;
+
+public final class Phase2Executor {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger(Phase2Executor.class);
+
+ private final EventBus eventBus;
+ private final Phases phases;
+ private final MavenPluginsConfigurator mavenPluginsConfigurator;
+ private final InitializersExecutor initializersExecutor;
+ private final SensorsExecutor sensorsExecutor;
+ private final SensorContext sensorContext;
+ private final ProjectInitializer pi;
+ private final FileSystemLogger fsLogger;
+ private final DefaultModuleFileSystem fs;
+ private final QProfileVerifier profileVerifier;
+ private final IssueExclusionsLoader issueExclusionsLoader;
+
+ public Phase2Executor(Phases phases,
+ MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
+ SensorsExecutor sensorsExecutor,
+ SensorContext sensorContext,
+ EventBus eventBus, ProjectInitializer pi,
+ FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+ IssueExclusionsLoader issueExclusionsLoader) {
+ this.phases = phases;
+ this.mavenPluginsConfigurator = mavenPluginsConfigurator;
+ this.initializersExecutor = initializersExecutor;
+ this.sensorsExecutor = sensorsExecutor;
+ this.sensorContext = sensorContext;
+ this.eventBus = eventBus;
+ this.pi = pi;
+ this.fsLogger = fsLogger;
+ this.fs = fs;
+ this.profileVerifier = profileVerifier;
+ this.issueExclusionsLoader = issueExclusionsLoader;
+ }
+
+ public static Collection<Class> getPhaseClasses() {
+ return Lists.<Class>newArrayList(SensorsExecutor.class, InitializersExecutor.class, ProjectInitializer.class);
+ }
+
+ /**
+ * Executed on each module
+ */
+ public void execute(Project module) {
+ pi.execute(module);
+
+ eventBus.fireEvent(new ProjectAnalysisEvent(module, true));
+
+ executeMavenPhase(module);
+
+ executeInitializersPhase();
+
+ // Index and lock the filesystem
+ fs.index();
+
+ // Log detected languages and their profiles after FS is indexed and languages detected
+ profileVerifier.execute();
+
+ // Initialize issue exclusions
+ issueExclusionsLoader.execute();
+
+ sensorsExecutor.execute(sensorContext);
+
+ eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
+ }
+
+ private void executeInitializersPhase() {
+ if (phases.isEnabled(Phases.Phase.INIT)) {
+ initializersExecutor.execute();
+ fsLogger.log();
+ }
+ }
+
+ private void executeMavenPhase(Project module) {
+ if (phases.isEnabled(Phases.Phase.MAVEN)) {
+ eventBus.fireEvent(new MavenPhaseEvent(true));
+ mavenPluginsConfigurator.execute(module);
+ eventBus.fireEvent(new MavenPhaseEvent(false));
+ }
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
index f1700a38fcb..027abd427eb 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
@@ -119,12 +119,12 @@ public class ProjectReactorBuilder {
}
protected ProjectDefinition defineProject(Properties properties, @Nullable ProjectDefinition parent) {
- File baseDir = new File(properties.getProperty(PROPERTY_PROJECT_BASEDIR));
if (properties.containsKey(PROPERTY_MODULES)) {
checkMandatoryProperties(properties, MANDATORY_PROPERTIES_FOR_MULTIMODULE_PROJECT);
} else {
checkMandatoryProperties(properties, MANDATORY_PROPERTIES_FOR_SIMPLE_PROJECT);
}
+ File baseDir = new File(properties.getProperty(PROPERTY_PROJECT_BASEDIR));
final String projectKey = properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY);
File workDir;
if (parent == null) {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java
new file mode 100644
index 00000000000..44bcadc309c
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java
@@ -0,0 +1,211 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.scan2;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.rule.CheckFactory;
+import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.resources.Project;
+import org.sonar.api.scan.filesystem.FileExclusions;
+import org.sonar.batch.DefaultProjectClasspath;
+import org.sonar.batch.DefaultSensorContext;
+import org.sonar.batch.DefaultTimeMachine;
+import org.sonar.batch.ProjectTree;
+import org.sonar.batch.ResourceFilters;
+import org.sonar.batch.ViolationFilters;
+import org.sonar.batch.bootstrap.BatchExtensionDictionnary;
+import org.sonar.batch.bootstrap.ExtensionInstaller;
+import org.sonar.batch.bootstrap.ExtensionMatcher;
+import org.sonar.batch.bootstrap.ExtensionUtils;
+import org.sonar.batch.components.TimeMachineConfiguration;
+import org.sonar.batch.events.EventBus;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.index.ResourcePersister;
+import org.sonar.batch.issue.IssuableFactory;
+import org.sonar.batch.issue.IssueFilters;
+import org.sonar.batch.issue.ModuleIssues;
+import org.sonar.batch.issue.ignore.EnforceIssuesFilter;
+import org.sonar.batch.issue.ignore.IgnoreIssuesFilter;
+import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer;
+import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.batch.issue.ignore.scanner.IssueExclusionsRegexpScanner;
+import org.sonar.batch.language.LanguageDistributionDecorator;
+import org.sonar.batch.phases.Phase2Executor;
+import org.sonar.batch.phases.PhaseExecutor;
+import org.sonar.batch.phases.PhasesTimeProfiler;
+import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
+import org.sonar.batch.qualitygate.QualityGateProvider;
+import org.sonar.batch.qualitygate.QualityGateVerifier;
+import org.sonar.batch.rule.ActiveRulesProvider;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.rule.QProfileDecorator;
+import org.sonar.batch.rule.QProfileEventsDecorator;
+import org.sonar.batch.rule.QProfileSensor;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.rule.RulesProfileProvider;
+import org.sonar.batch.scan.LanguageVerifier;
+import org.sonar.batch.scan.ModuleSettings;
+import org.sonar.batch.scan.filesystem.ComponentIndexer;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.filesystem.DeprecatedFileFilters;
+import org.sonar.batch.scan.filesystem.ExclusionFilters;
+import org.sonar.batch.scan.filesystem.FileIndexer;
+import org.sonar.batch.scan.filesystem.FileSystemLogger;
+import org.sonar.batch.scan.filesystem.InputFileBuilderFactory;
+import org.sonar.batch.scan.filesystem.LanguageDetectionFactory;
+import org.sonar.batch.scan.filesystem.ModuleFileSystemInitializer;
+import org.sonar.batch.scan.filesystem.ModuleInputFileCache;
+import org.sonar.batch.scan.filesystem.PreviousFileHashLoader;
+import org.sonar.batch.scan.filesystem.ProjectFileSystemAdapter;
+import org.sonar.batch.scan.filesystem.StatusDetectionFactory;
+import org.sonar.batch.scan.report.JsonReport;
+import org.sonar.core.component.ScanPerspectives;
+import org.sonar.core.measure.MeasurementFilters;
+
+public class ModuleScanContainer extends ComponentContainer {
+ private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class);
+ private final Project module;
+
+ public ModuleScanContainer(ProjectScanContainer parent, Project module) {
+ super(parent);
+ this.module = module;
+ }
+
+ @Override
+ protected void doBeforeStart() {
+ LOG.info("------------- Scan {}", module.getName());
+ addCoreComponents();
+ addExtensions();
+ }
+
+ private void addCoreComponents() {
+ ProjectDefinition moduleDefinition = getComponentByType(ProjectTree.class).getProjectDefinition(module);
+ add(
+ moduleDefinition,
+ module.getConfiguration(),
+ module,
+ ModuleSettings.class);
+
+ // hack to initialize commons-configuration before ExtensionProviders
+ getComponentByType(ModuleSettings.class);
+
+ add(
+ EventBus.class,
+ Phase2Executor.class,
+ PhasesTimeProfiler.class,
+ Phase2Executor.getPhaseClasses(),
+ moduleDefinition.getContainerExtensions(),
+
+ // file system
+ ModuleInputFileCache.class,
+ FileExclusions.class,
+ ExclusionFilters.class,
+ DeprecatedFileFilters.class,
+ InputFileBuilderFactory.class,
+ StatusDetectionFactory.class,
+ LanguageDetectionFactory.class,
+ PreviousFileHashLoader.class,
+ FileIndexer.class,
+ ComponentIndexer.class,
+ LanguageVerifier.class,
+ FileSystemLogger.class,
+ DefaultProjectClasspath.class,
+ DefaultModuleFileSystem.class,
+ ModuleFileSystemInitializer.class,
+ ProjectFileSystemAdapter.class,
+ QProfileVerifier.class,
+
+ // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor)
+ getComponentByType(ResourcePersister.class).getSnapshot(module),
+
+ TimeMachineConfiguration.class,
+ DefaultSensorContext.class,
+ BatchExtensionDictionnary.class,
+ DefaultTimeMachine.class,
+ ViolationFilters.class,
+ IssueFilters.class,
+ MeasurementFilters.class,
+ ResourceFilters.class,
+
+ // quality gates
+ new QualityGateProvider(),
+ QualityGateVerifier.class,
+ GenerateQualityGateEvents.class,
+
+ // rules
+ ModuleQProfiles.class,
+ new ActiveRulesProvider(),
+ new RulesProfileProvider(),
+ QProfileSensor.class,
+ QProfileDecorator.class,
+ QProfileEventsDecorator.class,
+ CheckFactory.class,
+
+ // report
+ JsonReport.class,
+
+ // issues
+ IssuableFactory.class,
+ ModuleIssues.class,
+
+ // issue exclusions
+ IssueInclusionPatternInitializer.class,
+ IssueExclusionPatternInitializer.class,
+ IssueExclusionsRegexpScanner.class,
+ IssueExclusionsLoader.class,
+ EnforceIssuesFilter.class,
+ IgnoreIssuesFilter.class,
+
+ // language
+ LanguageDistributionDecorator.class,
+
+ ScanPerspectives.class);
+ }
+
+ private void addExtensions() {
+ ExtensionInstaller installer = getComponentByType(ExtensionInstaller.class);
+ installer.install(this, new ExtensionMatcher() {
+ public boolean accept(Object extension) {
+ if (ExtensionUtils.isType(extension, BatchComponent.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) {
+ // Special use-case: the extension point ProjectBuilder is used in a Maven environment to define some
+ // new sub-projects without pom.
+ // Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom.
+ return !ExtensionUtils.isMavenExtensionOnly(extension) || module.getPom() != null;
+ }
+ return false;
+ }
+ });
+ }
+
+ @Override
+ protected void doAfterStart() {
+ DefaultIndex index = getComponentByType(DefaultIndex.class);
+ index.setCurrentProject(module,
+ getComponentByType(ModuleIssues.class));
+
+ getComponentByType(PhaseExecutor.class).execute(module);
+ }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
new file mode 100644
index 00000000000..3ca92f4bc5b
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java
@@ -0,0 +1,229 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.scan2;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.config.Settings;
+import org.sonar.api.platform.ComponentContainer;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.resources.Project;
+import org.sonar.api.scan.filesystem.PathResolver;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.DefaultFileLinesContextFactory;
+import org.sonar.batch.ProjectConfigurator;
+import org.sonar.batch.ProjectTree;
+import org.sonar.batch.bootstrap.ExtensionInstaller;
+import org.sonar.batch.bootstrap.ExtensionMatcher;
+import org.sonar.batch.bootstrap.ExtensionUtils;
+import org.sonar.batch.bootstrap.MetricProvider;
+import org.sonar.batch.components.PeriodsDefinition;
+import org.sonar.batch.debt.DebtModelProvider;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.ComponentDataPersister;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.index.DefaultPersistenceManager;
+import org.sonar.batch.index.DefaultResourcePersister;
+import org.sonar.batch.index.DependencyPersister;
+import org.sonar.batch.index.EventPersister;
+import org.sonar.batch.index.LinkPersister;
+import org.sonar.batch.index.MeasurePersister;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.index.ResourceKeyMigration;
+import org.sonar.batch.index.SnapshotCache;
+import org.sonar.batch.index.SourcePersister;
+import org.sonar.batch.issue.DefaultProjectIssues;
+import org.sonar.batch.issue.DeprecatedViolations;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.issue.IssuePersister;
+import org.sonar.batch.issue.ScanIssueStorage;
+import org.sonar.batch.phases.GraphPersister;
+import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
+import org.sonar.batch.rule.RulesProvider;
+import org.sonar.batch.scan.ProjectReactorBuilder;
+import org.sonar.batch.scan.ProjectSettingsReady;
+import org.sonar.batch.scan.filesystem.InputFileCache;
+import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
+import org.sonar.batch.scan.maven.MavenPluginExecutor;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.batch.source.HighlightableBuilder;
+import org.sonar.batch.source.SymbolizableBuilder;
+import org.sonar.core.component.ScanGraph;
+import org.sonar.core.issue.IssueNotifications;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.workflow.FunctionExecutor;
+import org.sonar.core.issue.workflow.IssueWorkflow;
+import org.sonar.core.notification.DefaultNotificationManager;
+import org.sonar.core.technicaldebt.DefaultTechnicalDebtModel;
+import org.sonar.core.test.TestPlanBuilder;
+import org.sonar.core.test.TestPlanPerspectiveLoader;
+import org.sonar.core.test.TestableBuilder;
+import org.sonar.core.test.TestablePerspectiveLoader;
+import org.sonar.core.user.DefaultUserFinder;
+
+public class ProjectScanContainer extends ComponentContainer {
+ public ProjectScanContainer(ComponentContainer taskContainer) {
+ super(taskContainer);
+ }
+
+ @Override
+ protected void doBeforeStart() {
+ projectBootstrap();
+ addBatchComponents();
+ fixMavenExecutor();
+ addBatchExtensions();
+ Settings settings = getComponentByType(Settings.class);
+ if (settings != null && settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
+ add(PhasesSumUpTimeProfiler.class);
+ }
+ }
+
+ private void projectBootstrap() {
+ // Old versions of bootstrappers used to pass project reactor as an extension
+ // so check if it is already present in parent container
+ ProjectReactor reactor = getComponentByType(ProjectReactor.class);
+ if (reactor == null) {
+ // OK, not present, so look for a custom ProjectBootstrapper
+ ProjectBootstrapper bootstrapper = getComponentByType(ProjectBootstrapper.class);
+ Settings settings = getComponentByType(Settings.class);
+ if (bootstrapper == null
+ // Starting from Maven plugin 2.3 then only DefaultProjectBootstrapper should be used.
+ || "true".equals(settings.getString("sonar.mojoUseRunner"))) {
+ // Use default SonarRunner project bootstrapper
+ ProjectReactorBuilder builder = getComponentByType(ProjectReactorBuilder.class);
+ reactor = builder.execute();
+ } else {
+ reactor = bootstrapper.bootstrap();
+ }
+ if (reactor == null) {
+ throw new SonarException(bootstrapper + " has returned null as ProjectReactor");
+ }
+ add(reactor);
+ }
+ }
+
+ private void addBatchComponents() {
+ add(
+ DefaultPersistenceManager.class,
+ DependencyPersister.class,
+ EventPersister.class,
+ LinkPersister.class,
+ MeasurePersister.class,
+ DefaultResourcePersister.class,
+ SourcePersister.class,
+ DefaultNotificationManager.class,
+ MetricProvider.class,
+ ProjectConfigurator.class,
+ DefaultIndex.class,
+ ResourceKeyMigration.class,
+ DefaultFileLinesContextFactory.class,
+ Caches.class,
+ SnapshotCache.class,
+ ResourceCache.class,
+ ComponentDataCache.class,
+ ComponentDataPersister.class,
+ DefaultUserFinder.class,
+
+ // file system
+ InputFileCache.class,
+ PathResolver.class,
+
+ // issues
+ IssueUpdater.class,
+ FunctionExecutor.class,
+ IssueWorkflow.class,
+ DeprecatedViolations.class,
+ IssueCache.class,
+ ScanIssueStorage.class,
+ IssuePersister.class,
+ IssueNotifications.class,
+ DefaultProjectIssues.class,
+ IssueChangelogDebtCalculator.class,
+
+ // tests
+ TestPlanPerspectiveLoader.class,
+ TestablePerspectiveLoader.class,
+ TestPlanBuilder.class,
+ TestableBuilder.class,
+ ScanGraph.create(),
+ GraphPersister.class,
+
+ // lang
+ Languages.class,
+ HighlightableBuilder.class,
+ SymbolizableBuilder.class,
+
+ // technical debt
+ DefaultTechnicalDebtModel.class,
+ new DebtModelProvider(),
+
+ // rules
+ new RulesProvider(),
+
+ // Differential periods
+ PeriodsDefinition.class,
+
+ // Measures
+ MeasureCache.class,
+
+ ProjectSettingsReady.class);
+ }
+
+ private void fixMavenExecutor() {
+ if (getComponentByType(MavenPluginExecutor.class) == null) {
+ add(FakeMavenPluginExecutor.class);
+ }
+ }
+
+ private void addBatchExtensions() {
+ getComponentByType(ExtensionInstaller.class).install(this, new BatchExtensionFilter());
+ }
+
+ @Override
+ protected void doAfterStart() {
+ ProjectTree tree = getComponentByType(ProjectTree.class);
+ scanRecursively(tree.getRootProject());
+ }
+
+ private void scanRecursively(Project module) {
+ for (Project subModules : module.getModules()) {
+ scanRecursively(subModules);
+ }
+ scan(module);
+ }
+
+ @VisibleForTesting
+ void scan(Project module) {
+ new ModuleScanContainer(this, module).execute();
+ }
+
+ static class BatchExtensionFilter implements ExtensionMatcher {
+ public boolean accept(Object extension) {
+ return ExtensionUtils.isType(extension, BatchComponent.class)
+ && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH);
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
index 2e29bf1f8e2..9153769ff23 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
@@ -68,8 +68,8 @@ public class BatchPluginRepositoryTest {
public void shouldLoadPlugin() throws Exception {
RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
- PluginDownloader downloader = mock(PluginDownloader.class);
- when(downloader.downloadPlugin(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar"));
+ DefaultPluginsReferential downloader = mock(DefaultPluginsReferential.class);
+ when(downloader.pluginFile(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar"));
repository = new BatchPluginRepository(downloader, new Settings(), mode, new BatchPluginJarInstaller(cache));
@@ -86,9 +86,9 @@ public class BatchPluginRepositoryTest {
RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
RemotePlugin checkstyleExt = new RemotePlugin("checkstyleextensions", false);
- PluginDownloader downloader = mock(PluginDownloader.class);
- when(downloader.downloadPlugin(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar"));
- when(downloader.downloadPlugin(checkstyleExt)).thenReturn(fileFromCache("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
+ DefaultPluginsReferential downloader = mock(DefaultPluginsReferential.class);
+ when(downloader.pluginFile(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar"));
+ when(downloader.pluginFile(checkstyleExt)).thenReturn(fileFromCache("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
repository = new BatchPluginRepository(downloader, new Settings(), mode, new BatchPluginJarInstaller(cache));
@@ -106,9 +106,9 @@ public class BatchPluginRepositoryTest {
RemotePlugin checkstyle = new RemotePlugin("checkstyle", true);
RemotePlugin checkstyleExt = new RemotePlugin("checkstyleextensions", false);
- PluginDownloader downloader = mock(PluginDownloader.class);
- when(downloader.downloadPlugin(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar"));
- when(downloader.downloadPlugin(checkstyleExt)).thenReturn(fileFromCache("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
+ DefaultPluginsReferential downloader = mock(DefaultPluginsReferential.class);
+ when(downloader.pluginFile(checkstyle)).thenReturn(fileFromCache("sonar-checkstyle-plugin-2.8.jar"));
+ when(downloader.pluginFile(checkstyleExt)).thenReturn(fileFromCache("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"));
Settings settings = new Settings();
settings.setProperty(CoreProperties.BATCH_EXCLUDE_PLUGINS, "checkstyle");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PluginDownloaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PluginDownloaderTest.java
index 4c8120b903e..acb2a29c46c 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PluginDownloaderTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/PluginDownloaderTest.java
@@ -23,7 +23,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-import org.sonar.api.utils.SonarException;
import org.sonar.core.plugins.RemotePlugin;
import org.sonar.home.cache.FileCache;
@@ -50,9 +49,9 @@ public class PluginDownloaderTest {
FileCache cache = mock(FileCache.class);
ServerClient server = mock(ServerClient.class);
when(server.request("/deploy/plugins/index.txt")).thenReturn("checkstyle,true\nsqale,false");
- PluginDownloader downloader = new PluginDownloader(cache, server);
+ DefaultPluginsReferential downloader = new DefaultPluginsReferential(cache, server);
- List<RemotePlugin> plugins = downloader.downloadPluginIndex();
+ List<RemotePlugin> plugins = downloader.pluginList();
assertThat(plugins).hasSize(2);
assertThat(plugins.get(0).getKey()).isEqualTo("checkstyle");
assertThat(plugins.get(0).isCore()).isTrue();
@@ -68,22 +67,22 @@ public class PluginDownloaderTest {
when(cache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar);
ServerClient server = mock(ServerClient.class);
- PluginDownloader downloader = new PluginDownloader(cache, server);
+ DefaultPluginsReferential downloader = new DefaultPluginsReferential(cache, server);
RemotePlugin plugin = new RemotePlugin("checkstyle", true)
.setFile("checkstyle-plugin.jar", "fakemd5_1");
- File file = downloader.downloadPlugin(plugin);
+ File file = downloader.pluginFile(plugin);
assertThat(file).isEqualTo(pluginJar);
}
@Test
public void should_fail_to_get_plugin_index() throws Exception {
- thrown.expect(SonarException.class);
+ thrown.expect(IllegalStateException.class);
ServerClient server = mock(ServerClient.class);
- doThrow(new SonarException()).when(server).request("/deploy/plugins/index.txt");
+ doThrow(new IllegalStateException()).when(server).request("/deploy/plugins/index.txt");
- new PluginDownloader(mock(FileCache.class), server).downloadPluginIndex();
+ new DefaultPluginsReferential(mock(FileCache.class), server).pluginList();
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java
new file mode 100644
index 00000000000..ae4a9ec02bb
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java
@@ -0,0 +1,168 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.batch.medium;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.MetricFinder;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RuleQuery;
+import org.sonar.batch.bootstrap.PluginsReferential;
+import org.sonar.batch.bootstrapper.Batch;
+import org.sonar.batch.bootstrapper.EnvironmentInformation;
+import org.sonar.batch.settings.SettingsReferential;
+import org.sonar.core.plugins.RemotePlugin;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Scan2MediumTest {
+
+ @Test
+ public void mediumTest() {
+ Batch batch = Batch.builder()
+ .setEnableLoggingConfiguration(true)
+ .addComponent(new EnvironmentInformation("mediumTest", "1.0"))
+ .addComponent(new MockSettingsReferential())
+ .addComponent(new MockPluginsReferential())
+ .addComponent(new MockMetricFinder())
+ .addComponent(new MockRuleFinder())
+ .setBootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor"))
+ .build();
+
+ batch.start();
+
+ // batch.executeTask(ImmutableMap.<String, String>builder().put("sonar.task", "scan").build());
+
+ batch.stop();
+ }
+
+ private static class MockSettingsReferential implements SettingsReferential {
+
+ private Map<String, String> globalSettings = new HashMap<String, String>();
+ private Map<String, Map<String, String>> projectSettings = new HashMap<String, Map<String, String>>();
+
+ @Override
+ public Map<String, String> globalSettings() {
+ return globalSettings;
+ }
+
+ @Override
+ public Map<String, String> projectSettings(String projectKey) {
+ return projectSettings.containsKey(projectKey) ? projectSettings.get(projectKey) : Collections.<String, String>emptyMap();
+ }
+
+ }
+
+ private static class MockPluginsReferential implements PluginsReferential {
+
+ private List<RemotePlugin> pluginList = new ArrayList<RemotePlugin>();
+ private Map<RemotePlugin, File> pluginFiles = new HashMap<RemotePlugin, File>();
+
+ @Override
+ public List<RemotePlugin> pluginList() {
+ return pluginList;
+ }
+
+ @Override
+ public File pluginFile(RemotePlugin remote) {
+ return pluginFiles.get(remote);
+ }
+
+ }
+
+ private static class MockMetricFinder implements MetricFinder {
+
+ private Map<String, Metric> metricsByKey = Maps.newLinkedHashMap();
+ private Map<Integer, Metric> metricsById = Maps.newLinkedHashMap();
+
+ @Override
+ public Metric findById(int metricId) {
+ return metricsById.get(metricId);
+ }
+
+ @Override
+ public Metric findByKey(String key) {
+ return metricsByKey.get(key);
+ }
+
+ @Override
+ public Collection<Metric> findAll(List<String> metricKeys) {
+ List<Metric> result = Lists.newLinkedList();
+ for (String metricKey : metricKeys) {
+ Metric metric = findByKey(metricKey);
+ if (metric != null) {
+ result.add(metric);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Collection<Metric> findAll() {
+ return metricsByKey.values();
+ }
+
+ }
+
+ private static class MockRuleFinder implements RuleFinder {
+ private BiMap<Integer, Rule> rulesById = HashBiMap.create();
+ private Map<String, Map<String, Rule>> rulesByRepoKeyAndRuleKey = Maps.newHashMap();
+
+ @Override
+ public Rule findById(int ruleId) {
+ return rulesById.get(ruleId);
+ }
+
+ @Override
+ public Rule findByKey(String repositoryKey, String ruleKey) {
+ Map<String, Rule> repository = rulesByRepoKeyAndRuleKey.get(repositoryKey);
+ return repository != null ? repository.get(ruleKey) : null;
+ }
+
+ @Override
+ public Rule findByKey(RuleKey key) {
+ return findByKey(key.repository(), key.rule());
+ }
+
+ @Override
+ public Rule find(RuleQuery query) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<Rule> findAll(RuleQuery query) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
index 2b105f454fe..ee0d032f215 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -510,6 +510,11 @@ public interface CoreProperties {
String ANALYSIS_MODE_INCREMENTAL = "incremental";
/**
+ * @since 4.4
+ */
+ String ANALYSIS_MODE_SENSOR = "sensor";
+
+ /**
* @since 4.0
*/
String PREVIEW_INCLUDE_PLUGINS = "sonar.preview.includePlugins";
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleFinder.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleFinder.java
index 60a1f2f83c3..6e93bf5b7fe 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleFinder.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleFinder.java
@@ -19,12 +19,12 @@
*/
package org.sonar.api.rules;
+import org.sonar.api.ServerComponent;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.task.TaskComponent;
-import org.sonar.api.ServerComponent;
-
import javax.annotation.CheckForNull;
+
import java.util.Collection;
/**
@@ -44,6 +44,10 @@ public interface RuleFinder extends TaskComponent, ServerComponent {
@CheckForNull
Rule findByKey(RuleKey key);
+ /**
+ * @throw NonUniqueResultException if more than one result
+ */
+ @CheckForNull
Rule find(RuleQuery query);
Collection<Rule> findAll(RuleQuery query);