]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5389 Initial version of the new sensor mode
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 9 Jun 2014 22:41:17 +0000 (00:41 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 10 Jun 2014 10:36:11 +0000 (12:36 +0200)
16 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BootstrapContainer.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultPluginsReferential.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginDownloader.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginsReferential.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/bootstrap/TaskContainer.java
sonar-batch/src/main/java/org/sonar/batch/phases/Phase2Executor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
sonar-batch/src/main/java/org/sonar/batch/scan2/ModuleScanContainer.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BatchPluginRepositoryTest.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/PluginDownloaderTest.java
sonar-batch/src/test/java/org/sonar/batch/medium/Scan2MediumTest.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleFinder.java

index eb8468ba5b6f3a0c08a68d8587a0011531ee3c54..0b22c904e5999b7d3e75d6252ec5ff86b4c532bc 100644 (file)
@@ -119,13 +119,6 @@ import org.sonar.plugins.core.widgets.measures.MeasureFilterTreemapWidget;
 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,
index 2e2b09da23cc96571f5540f48931676575629db4..aea375f3c0d8d3d776020818d5833f148926144a 100644 (file)
@@ -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);
index feffc2f6ede78bd44d94646cc9769c485f50f48c..992ccf77819fbe5d414115cc04fa94d5240972e1 100644 (file)
 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/DefaultPluginsReferential.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultPluginsReferential.java
new file mode 100644 (file)
index 0000000..93ae4e9
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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 com.google.common.collect.Lists;
+import org.apache.commons.lang.CharUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.core.plugins.RemotePlugin;
+import org.sonar.core.plugins.RemotePluginFile;
+import org.sonar.home.cache.FileCache;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * A {@link PluginsReferential} implementation that put downloaded plugins in a FS cache.
+ */
+public class DefaultPluginsReferential implements PluginsReferential {
+
+  private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginsReferential.class);
+
+  private ServerClient server;
+  private FileCache fileCache;
+
+  public DefaultPluginsReferential(FileCache fileCache, ServerClient server) {
+    this.server = server;
+    this.fileCache = fileCache;
+  }
+
+  @Override
+  public File pluginFile(final RemotePlugin remote) {
+    try {
+      final RemotePluginFile file = remote.file();
+      File cachedFile = fileCache.get(file.getFilename(), file.getHash(), new FileCache.Downloader() {
+        public void download(String filename, File toFile) throws IOException {
+          String url = "/deploy/plugins/" + remote.getKey() + "/" + file.getFilename();
+          if (LOG.isDebugEnabled()) {
+            LOG.debug("Download {} to {}", url, toFile.getAbsolutePath());
+          } else {
+            LOG.info("Download {}", file.getFilename());
+          }
+          server.download(url, toFile);
+        }
+      });
+      return cachedFile;
+
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to download plugin: " + remote.getKey(), e);
+    }
+  }
+
+  @Override
+  public List<RemotePlugin> pluginList() {
+    String url = "/deploy/plugins/index.txt";
+    try {
+      LOG.debug("Download index of plugins");
+      String indexContent = server.request(url);
+      String[] rows = StringUtils.split(indexContent, CharUtils.LF);
+      List<RemotePlugin> remoteLocations = Lists.newArrayList();
+      for (String row : rows) {
+        remoteLocations.add(RemotePlugin.unmarshal(row));
+      }
+      return remoteLocations;
+
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to download plugins index: " + url, e);
+    }
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginDownloader.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/PluginDownloader.java
deleted file mode 100644 (file)
index 0af5f2a..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 com.google.common.collect.Lists;
-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;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-public class PluginDownloader implements BatchComponent {
-
-  private static final Logger LOG = LoggerFactory.getLogger(PluginDownloader.class);
-
-  private ServerClient server;
-  private FileCache fileCache;
-
-  public PluginDownloader(FileCache fileCache, ServerClient server) {
-    this.server = server;
-    this.fileCache = fileCache;
-  }
-
-  public File downloadPlugin(final RemotePlugin remote) {
-    try {
-      final RemotePluginFile file = remote.file();
-      File cachedFile = fileCache.get(file.getFilename(), file.getHash(), new FileCache.Downloader() {
-        public void download(String filename, File toFile) throws IOException {
-          String url = "/deploy/plugins/" + remote.getKey() + "/" + file.getFilename();
-          if (LOG.isDebugEnabled()) {
-            LOG.debug("Download {} to {}", url, toFile.getAbsolutePath());
-          } else {
-            LOG.info("Download {}", file.getFilename());
-          }
-          server.download(url, toFile);
-        }
-      });
-      return cachedFile;
-
-    } catch (Exception e) {
-      throw new SonarException("Fail to download plugin: " + remote.getKey(), e);
-    }
-  }
-
-  public List<RemotePlugin> downloadPluginIndex() {
-    String url = "/deploy/plugins/index.txt";
-    try {
-      LOG.debug("Download index of plugins");
-      String indexContent = server.request(url);
-      String[] rows = StringUtils.split(indexContent, CharUtils.LF);
-      List<RemotePlugin> remoteLocations = Lists.newArrayList();
-      for (String row : rows) {
-        remoteLocations.add(RemotePlugin.unmarshal(row));
-      }
-      return remoteLocations;
-
-    } catch (Exception e) {
-      throw new SonarException("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 (file)
index 0000000..daf38ae
--- /dev/null
@@ -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);
+
+}
index 4bdda269f46894c9bd100ccf07d373a30959ab44..86122e7dec45704d8c5780bd0dc63e713df93ec0 100644 (file)
@@ -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 (file)
index 0000000..87d1348
--- /dev/null
@@ -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));
+    }
+  }
+}
index f1700a38fcbadbfb97d32790603c30b947d6332a..027abd427eb77053b22a1429a241afefb23ba31b 100644 (file)
@@ -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 (file)
index 0000000..44bcadc
--- /dev/null
@@ -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 (file)
index 0000000..3ca92f4
--- /dev/null
@@ -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);
+    }
+  }
+}
index 2e29bf1f8e2ca50f239e93f26f79a78158472126..9153769ff23a213d3e5638fa82813ff7482dd955 100644 (file)
@@ -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");
index 4c8120b903e052216734c103b00a297be70397bd..acb2a29c46cc03059548607edf7e444320704ad2 100644 (file)
@@ -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 (file)
index 0000000..ae4a9ec
--- /dev/null
@@ -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();
+    }
+  }
+
+}
index 2b105f454fe11ac09798f9888e90163690dbfb88..ee0d032f215b5bd87797ad0c26a8462f765c42c1 100644 (file)
@@ -509,6 +509,11 @@ public interface CoreProperties {
    */
   String ANALYSIS_MODE_INCREMENTAL = "incremental";
 
+  /**
+   * @since 4.4
+   */
+  String ANALYSIS_MODE_SENSOR = "sensor";
+
   /**
    * @since 4.0
    */
index 60a1f2f83c30d04c1f84ea655761d217b95fdce8..6e93bf5b7fee974328a1343c645d1fb044929791 100644 (file)
  */
 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);