]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6817 Issues mode should support analysis of projects not associated
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Tue, 8 Sep 2015 13:24:50 +0000 (15:24 +0200)
committerDuarte Meneses <duarte.meneses@sonarsource.com>
Wed, 9 Sep 2015 09:57:38 +0000 (11:57 +0200)
78 files changed:
it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties [new file with mode: 0644]
it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo [new file with mode: 0644]
it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures [new file with mode: 0644]
it/it-tests/src/test/java/batch/IssuesModeTest.java
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/ProjectRepositories.java
sonar-batch/src/main/java/org/sonar/batch/analysis/DefaultAnalysisMode.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ServerClient.java
sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java
sonar-batch/src/main/java/org/sonar/batch/cache/DefaultProjectCacheStatus.java
sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/cache/PersistentCacheProvider.java
sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheStatus.java
sonar-batch/src/main/java/org/sonar/batch/cache/ProjectCacheSynchronizer.java
sonar-batch/src/main/java/org/sonar/batch/cache/ProjectSyncContainer.java
sonar-batch/src/main/java/org/sonar/batch/cache/WSLoader.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoader.java
sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java
sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleSettings.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectLock.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorBuilder.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectSettings.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetection.java
sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/StatusDetectionFactory.java
sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java
sonar-batch/src/test/java/org/sonar/batch/cache/DefaultProjectCacheStatusTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/cache/PersistentCacheProviderTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/ProjectCacheSynchronizerTest.java
sonar-batch/src/test/java/org/sonar/batch/cache/ProjectSyncContainerTest.java
sonar-batch/src/test/java/org/sonar/batch/issue/tracking/DefaultServerLineHashesLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/BatchMediumTester.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/cache/CacheSyncTest.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/scan/ModuleSettingsTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorBuilderTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectSettingsTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionFactoryTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/StatusDetectionTest.java
sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo [new file with mode: 0644]
sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text [deleted file]
sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text [deleted file]
sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties [new file with mode: 0644]
sonar-home/src/main/java/org/sonar/home/cache/FileCache.java
sonar-home/src/main/java/org/sonar/home/cache/FileHashes.java
sonar-home/src/main/java/org/sonar/home/cache/PersistentCache.java

diff --git a/it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties b/it/it-projects/shared/xoo-sample-non-associated/sonar-project.properties
new file mode 100644 (file)
index 0000000..71b83e4
--- /dev/null
@@ -0,0 +1,5 @@
+#sonar.projectKey=sample
+sonar.projectName=Sample
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.sources=src/main/xoo
+sonar.language=xoo
diff --git a/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo b/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo
new file mode 100644 (file)
index 0000000..41871e1
--- /dev/null
@@ -0,0 +1,16 @@
+package sample;
+
+public class Sample {
+       
+       public Sample(int i) {
+               int j = i++;
+       }
+       
+       private String myMethod() {
+               if (foo == bar) {
+                       return "hello";
+               } else {
+                       throw new IllegalStateException();
+               }
+       }
+}
diff --git a/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures b/it/it-projects/shared/xoo-sample-non-associated/src/main/xoo/sample/Sample.xoo.measures
new file mode 100644 (file)
index 0000000..3f73ea8
--- /dev/null
@@ -0,0 +1,11 @@
+ncloc:13
+#Used by dashboard/widgets tests
+complexity:3
+complexity_in_classes:3
+classes:1
+comment_lines:3
+public_api:5
+public_undocumented_api:2
+duplicated_files:1
+duplicated_blocks:2
+duplicated_lines:3
index 651546c0535006a2b4b9e1aa7d4d79968d25d79c..5e3c0660b669afeabf7eca468f9298aca24346f1 100644 (file)
@@ -69,28 +69,45 @@ public class IssuesModeTest {
   }
 
   @Test
-  public void issuesAnalysisOnNewProject() throws IOException {
+  public void issues_analysis_on_new_project() throws IOException {
     restoreProfile("one-issue-per-line.xml");
     orchestrator.getServer().provisionProject("sample", "xoo-sample");
     orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
     SonarRunner runner = configureRunnerIssues("shared/xoo-sample");
-    orchestrator.executeBuild(runner);
+    BuildResult result = orchestrator.executeBuild(runner);
+    assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
   }
-  
+
   @Test
-  public void invalidIncrementalMode() throws IOException {
+  public void invalid_incremental_mode() throws IOException {
     restoreProfile("one-issue-per-line.xml");
     orchestrator.getServer().provisionProject("sample", "xoo-sample");
     orchestrator.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line");
     SonarRunner runner = configureRunner("shared/xoo-sample");
     runner.setProperty("sonar.analysis.mode", "incremental");
-    
+
     thrown.expect(BuildFailureException.class);
     BuildResult res = orchestrator.executeBuild(runner);
-    
+
     assertThat(res.getLogs()).contains("Invalid analysis mode: incremental. This mode was removed in SonarQube 5.2");
   }
 
+  @Test
+  public void non_associated_mode() throws IOException {
+    restoreProfile("one-issue-per-line.xml");
+    setDefaultQualityProfile("xoo", "one-issue-per-line");
+    SonarRunner runner = configureRunnerIssues("shared/xoo-sample-non-associated");
+    BuildResult result = orchestrator.executeBuild(runner);
+    
+    assertThat(result.getLogs()).contains("is not associated");
+    assertThat(result.getLogs()).contains("Cache not found, synchronizing data");
+    assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+    
+    result = orchestrator.executeBuild(runner);
+    assertThat(ItUtils.countIssuesInJsonReport(result, true)).isEqualTo(17);
+    assertThat(result.getLogs()).contains("Found cache");
+  }
+
   // SONAR-5715
   @Test
   public void test_issues_mode_on_project_with_space_in_filename() throws IOException {
@@ -329,16 +346,16 @@ public class IssuesModeTest {
     boolean expectedError = false;
     for (Future<BuildResult> result : executorService.invokeAll(tasks)) {
       try {
-      result.get();
-      } catch(ExecutionException e) {
-        if(e.getCause() instanceof BuildFailureException) {
+        result.get();
+      } catch (ExecutionException e) {
+        if (e.getCause() instanceof BuildFailureException) {
           BuildFailureException bfe = (BuildFailureException) e.getCause();
           assertThat(bfe.getResult().getLogs()).contains("Another SonarQube analysis is already in progress for this project");
           expectedError = true;
         }
       }
     }
-    if(!expectedError) {
+    if (!expectedError) {
       fail("At least one of the threads should have failed");
     }
   }
@@ -370,4 +387,10 @@ public class IssuesModeTest {
     return runner;
   }
 
+  private void setDefaultQualityProfile(String languageKey, String profileName) {
+    orchestrator.getServer().adminWsClient().post("api/qualityprofiles/set_default",
+      "language", languageKey,
+      "profileName", profileName);
+  }
+
 }
index 8e59ccd1be777779ef5fb5cd745dd2fb1d13b270..862a21152d8cc7e2afae0f27200d0483c53e8ba4 100644 (file)
@@ -45,6 +45,10 @@ public class ProjectRepositories {
   public Map<String, String> settings(String moduleKey) {
     return settingsByModule.containsKey(moduleKey) ? settingsByModule.get(moduleKey) : Collections.<String, String>emptyMap();
   }
+  
+  public Map<String, Map<String, String>> settings() {
+    return settingsByModule;
+  }
 
   public ProjectRepositories addSettings(String moduleKey, Map<String, String> settings) {
     Map<String, String> existingSettings = settingsByModule.get(moduleKey);
index c49b4f4832752aef783f96fbc6c0f450451de8fc..a01928f35291d098e75d626699f7e1aac86a2214 100644 (file)
@@ -38,6 +38,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
   private static final Logger LOG = LoggerFactory.getLogger(DefaultAnalysisMode.class);
 
   private boolean mediumTestMode;
+  private boolean notAssociated;
 
   public DefaultAnalysisMode(GlobalProperties globalProps, AnalysisProperties props) {
     init(globalProps.properties(), props.properties());
@@ -47,6 +48,10 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
     return mediumTestMode;
   }
 
+  public boolean isNotAssociated() {
+    return notAssociated;
+  }
+
   private void init(Map<String, String> globalProps, Map<String, String> analysisProps) {
     // make sure analysis is consistent with global properties
     boolean globalPreview = isIssues(globalProps);
@@ -64,6 +69,7 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
     validate(mode);
     issues = CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode) || CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode);
     mediumTestMode = "true".equals(getPropertyWithFallback(analysisProps, globalProps, FakePluginInstaller.MEDIUM_TEST_ENABLED));
+    notAssociated = issues && rootProjectKeyMissing(analysisProps);
   }
 
   public void printMode() {
@@ -77,6 +83,9 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
     if (mediumTestMode) {
       LOG.info("Medium test mode");
     }
+    if (notAssociated) {
+      LOG.info("Project is not associated with the server");
+    }
   }
 
   private static String getPropertyWithFallback(Map<String, String> props1, Map<String, String> props2, String key) {
@@ -93,4 +102,9 @@ public class DefaultAnalysisMode extends AbstractAnalysisMode implements Analysi
     return CoreProperties.ANALYSIS_MODE_ISSUES.equals(mode);
   }
 
+  private static boolean rootProjectKeyMissing(Map<String, String> props) {
+    // ProjectReactorBuilder depends on this class, so it will only create this property later
+    return !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY);
+  }
+
 }
index 67e5843c8ee2a0d0ba41001ca6132a943ef46c6b..5469343c55dacf79d182571f85e4cb95cb92bd40 100644 (file)
@@ -123,14 +123,22 @@ public class GlobalContainer extends ComponentContainer {
   public void executeAnalysis(Map<String, String> analysisProperties, Object... components) {
     AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
     if (isIssuesMode(props)) {
-      new ProjectSyncContainer(this, props, false).execute();
+      String projectKey = getProjectKeyWithBranch(props);
+      new ProjectSyncContainer(this, projectKey, false).execute();
     }
     new ProjectScanContainer(this, props, components).execute();
   }
 
-  public void syncProject(Map<String, String> analysisProperties, boolean force) {
-    AnalysisProperties props = new AnalysisProperties(analysisProperties, this.getComponentByType(GlobalProperties.class).property(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
-    new ProjectSyncContainer(this, props, force).execute();
+  private static String getProjectKeyWithBranch(AnalysisProperties props) {
+    String projectKey = props.property(CoreProperties.PROJECT_KEY_PROPERTY);
+    if (projectKey != null && props.property(CoreProperties.PROJECT_BRANCH_PROPERTY) != null) {
+      projectKey = projectKey + ":" + props.property(CoreProperties.PROJECT_BRANCH_PROPERTY);
+    }
+    return projectKey;
+  }
+
+  public void syncProject(String projectKey, boolean force) {
+    new ProjectSyncContainer(this, projectKey, force).execute();
   }
 
   private boolean isIssuesMode(AnalysisProperties props) {
index 0faef08262771a2c621850c8bdf5aff2f035ab67..901e2d2cac22cb628fce3c73397b33db63bed9e9 100644 (file)
@@ -19,6 +19,9 @@
  */
 package org.sonar.batch.bootstrap;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -27,10 +30,13 @@ import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
@@ -55,8 +61,8 @@ import org.sonar.core.util.DefaultHttpDownloader;
  */
 @BatchSide
 public class ServerClient {
-
   private static final String GET = "GET";
+  private static final Logger LOG = LoggerFactory.getLogger(ServerClient.class);
   private GlobalProperties props;
   private DefaultHttpDownloader.BaseHttpDownloader downloader;
 
@@ -68,6 +74,20 @@ public class ServerClient {
   public String getURL() {
     return StringUtils.removeEnd(StringUtils.defaultIfBlank(props.property("sonar.host.url"), "http://localhost:9000"), "/");
   }
+  
+  public String getServerVersion() {
+    InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt");
+    if (is == null) {
+      LOG.warn("Failed to get SQ version");
+      return null;
+    }
+    try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
+      return br.readLine();
+    } catch (IOException e) {
+      LOG.warn("Failed to get SQ version", e);
+      return null;
+    }
+  }
 
   public URI getURI(String pathStartingWithSlash) {
     Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /: " + pathStartingWithSlash);
index 14070c17d216ae698a5385077354f6e8429a0685..e98e6366f7a1f7d47c712b4afec2af4361833583 100644 (file)
@@ -19,9 +19,6 @@
  */
 package org.sonar.batch.bootstrapper;
 
-import com.google.common.collect.ImmutableMap;
-
-import org.sonar.api.CoreProperties;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -134,8 +131,7 @@ public final class Batch {
    */
   public Batch syncProject(String projectKey) {
     checkStarted();
-    Map<String, String> props = ImmutableMap.of(CoreProperties.PROJECT_KEY_PROPERTY, projectKey);
-    bootstrapContainer.syncProject(props, true);
+    bootstrapContainer.syncProject(projectKey, true);
     return this;
   }
 
index 3308098c6a06534106b85ddec769b1c1693bcb7a..248d2b8b35065710290ee1685da9f62df8be43fa 100644 (file)
@@ -19,6 +19,8 @@
  */
 package org.sonar.batch.cache;
 
+import javax.annotation.Nullable;
+
 import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.home.cache.PersistentCache;
 
@@ -40,7 +42,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus {
   }
 
   @Override
-  public void save(String projectKey) {
+  public void save(@Nullable String projectKey) {
     Date now = new Date();
 
     try {
@@ -55,7 +57,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus {
   }
 
   @Override
-  public void delete(String projectKey) {
+  public void delete(@Nullable String projectKey) {
     try {
       cache.put(getKey(projectKey), new byte[0]);
     } catch (IOException e) {
@@ -64,7 +66,7 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus {
   }
 
   @Override
-  public Date getSyncStatus(String projectKey) {
+  public Date getSyncStatus(@Nullable String projectKey) {
     try {
       byte[] status = cache.get(getKey(projectKey), null);
       if (status == null || status.length == 0) {
@@ -79,7 +81,11 @@ public class DefaultProjectCacheStatus implements ProjectCacheStatus {
     }
   }
 
-  private String getKey(String projectKey) {
-    return STATUS_PREFIX + client.getURL() + "-" + projectKey;
+  private String getKey(@Nullable String projectKey) {
+    if (projectKey != null) {
+      return STATUS_PREFIX + client.getURL() + "-" + client.getServerVersion() + "-" + projectKey;
+    } else {
+      return STATUS_PREFIX + client.getURL() + "-" + client.getServerVersion();
+    }
   }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java b/sonar-batch/src/main/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizer.java
new file mode 100644 (file)
index 0000000..e21d142
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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.cache;
+
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.sonar.batch.protocol.input.QProfile;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+public class NonAssociatedCacheSynchronizer {
+  private static final Logger LOG = LoggerFactory.getLogger(NonAssociatedCacheSynchronizer.class);
+
+  private ProjectCacheStatus cacheStatus;
+  private QualityProfileLoader qualityProfileLoader;
+  private ActiveRulesLoader activeRulesLoader;
+
+  public NonAssociatedCacheSynchronizer(QualityProfileLoader qualityProfileLoader, ActiveRulesLoader activeRulesLoader, ProjectCacheStatus cacheStatus) {
+    this.qualityProfileLoader = qualityProfileLoader;
+    this.activeRulesLoader = activeRulesLoader;
+    this.cacheStatus = cacheStatus;
+  }
+
+  public void execute(boolean force) {
+    Date lastSync = cacheStatus.getSyncStatus(null);
+
+    if (lastSync != null) {
+      if (!force) {
+        LOG.info("Found cache [{}]", lastSync);
+        return;
+      } else {
+        LOG.info("-- Found cache [{}], synchronizing data..", lastSync);
+      }
+      cacheStatus.delete(null);
+    } else {
+      LOG.info("-- Cache not found, synchronizing data..");
+    }
+
+    loadData();
+    saveStatus();
+  }
+
+  private static Collection<String> getKeys(Collection<QProfile> qProfiles) {
+    List<String> list = new ArrayList<>(qProfiles.size());
+    for (QProfile qp : qProfiles) {
+      list.add(qp.key());
+    }
+
+    return list;
+  }
+
+  private void saveStatus() {
+    cacheStatus.save(null);
+    LOG.info("-- Succesfully synchronized cache");
+  }
+
+  private void loadData() {
+    Profiler profiler = Profiler.create(Loggers.get(ProjectCacheSynchronizer.class));
+
+    profiler.startInfo("Load default quality profiles");
+    Collection<QProfile> qProfiles = qualityProfileLoader.load(null, null);
+    profiler.stopInfo();
+
+    profiler.startInfo("Load default active rules");
+    activeRulesLoader.load(getKeys(qProfiles), null);
+    profiler.stopInfo();
+  }
+}
index 878554d249934861e54c110b042eb4b1505a570b..99040ba9927bf6777b42687cdc83bf255c50dae0 100644 (file)
@@ -49,14 +49,14 @@ public class PersistentCacheProvider extends ProviderAdapter {
         builder.setSonarHome(Paths.get(home));
       }
 
-      builder.setVersion(getVersion());
+      builder.setVersion(getServerVersion());
       cache = builder.build();
     }
 
     return cache;
   }
 
-  private String getVersion() {
+  private String getServerVersion() {
     InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt");
     if (is == null) {
       LOG.warn("Failed to get SQ version");
index 281cd60f34434505ba6f24adeae5732e8681f810..a5f97349a4b0903e15f42a5a41fc3bc8f9c31e51 100644 (file)
  */
 package org.sonar.batch.cache;
 
+import javax.annotation.Nullable;
+
 import java.util.Date;
 
 public interface ProjectCacheStatus {
-  void save(String projectKey);
+  void save(@Nullable String projectKey);
 
-  void delete(String projectKey);
+  void delete(@Nullable String projectKey);
 
-  Date getSyncStatus(String projectKey);
+  Date getSyncStatus(@Nullable String projectKey);
 }
index 6e6bccc9572e8869638c22095030c22739b201ad..d2d6cbcf2dbc6c2c43e7a0d697428eb9b074876d 100644 (file)
  */
 package org.sonar.batch.cache;
 
-import org.sonar.batch.analysis.AnalysisProperties;
+import com.google.common.base.Function;
 import org.apache.commons.lang.StringUtils;
-import org.sonar.api.utils.log.Loggers;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.log.Loggers;
 import org.sonar.api.utils.log.Profiler;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.batch.protocol.input.BatchInput.ServerIssue;
-import com.google.common.base.Function;
-import org.sonar.batch.protocol.input.FileData;
+import org.sonar.batch.protocol.input.QProfile;
+import org.sonar.batch.repository.ProjectSettingsLoader;
+import org.sonar.batch.repository.ProjectSettingsRepo;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.sonar.batch.repository.ServerIssuesLoader;
+import org.sonar.batch.repository.user.UserRepositoryLoader;
+import org.sonar.batch.rule.ActiveRulesLoader;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.List;
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import org.sonar.batch.protocol.input.ProjectRepositories;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.batch.repository.user.UserRepositoryLoader;
-import org.sonar.batch.issue.tracking.ServerLineHashesLoader;
-import org.sonar.batch.repository.ServerIssuesLoader;
-import org.sonar.batch.repository.ProjectRepositoriesLoader;
 
 public class ProjectCacheSynchronizer {
   private static final Logger LOG = LoggerFactory.getLogger(ProjectCacheSynchronizer.class);
-  private static final int NUM_THREAD = 2;
 
-  private final ProjectDefinition project;
-  private final AnalysisProperties properties;
-  private final ProjectRepositoriesLoader projectRepositoryLoader;
   private final ServerIssuesLoader issuesLoader;
-  private final ServerLineHashesLoader lineHashesLoader;
   private final UserRepositoryLoader userRepository;
   private final ProjectCacheStatus cacheStatus;
-
-  public ProjectCacheSynchronizer(ProjectReactor project, ProjectRepositoriesLoader projectRepositoryLoader, AnalysisProperties properties,
-    ServerIssuesLoader issuesLoader, ServerLineHashesLoader lineHashesLoader, UserRepositoryLoader userRepository, ProjectCacheStatus cacheStatus) {
-    this.project = project.getRoot();
-    this.projectRepositoryLoader = projectRepositoryLoader;
-    this.properties = properties;
+  private final QualityProfileLoader qualityProfileLoader;
+  private final ProjectSettingsLoader projectSettingsLoader;
+  private final ActiveRulesLoader activeRulesLoader;
+
+  public ProjectCacheSynchronizer(QualityProfileLoader qualityProfileLoader, ProjectSettingsLoader projectSettingsLoader,
+    ActiveRulesLoader activeRulesLoader, ServerIssuesLoader issuesLoader,
+    UserRepositoryLoader userRepository, ProjectCacheStatus cacheStatus) {
+    this.qualityProfileLoader = qualityProfileLoader;
+    this.projectSettingsLoader = projectSettingsLoader;
+    this.activeRulesLoader = activeRulesLoader;
     this.issuesLoader = issuesLoader;
-    this.lineHashesLoader = lineHashesLoader;
     this.userRepository = userRepository;
     this.cacheStatus = cacheStatus;
   }
 
-  public void load(boolean force) {
-    Date lastSync = cacheStatus.getSyncStatus(project.getKeyWithBranch());
+  public void load(String projectKey, boolean force) {
+    Date lastSync = cacheStatus.getSyncStatus(projectKey);
 
     if (lastSync != null) {
       if (!force) {
-        LOG.info("Found project [{}] cache [{}]", project.getKeyWithBranch(), lastSync);
+        LOG.info("Found project [{}] cache [{}]", projectKey, lastSync);
         return;
       } else {
-        LOG.info("-- Found project [{}] cache [{}], synchronizing data..", project.getKeyWithBranch(), lastSync);
+        LOG.info("-- Found project [{}] cache [{}], synchronizing data..", projectKey, lastSync);
       }
-      cacheStatus.delete(project.getKeyWithBranch());
+      cacheStatus.delete(projectKey);
     } else {
-      LOG.info("-- Cache for project [{}] not found, synchronizing data..", project.getKeyWithBranch());
+      LOG.info("-- Cache for project [{}] not found, synchronizing data..", projectKey);
     }
 
-    loadData();
-    saveStatus();
+    loadData(projectKey);
+    saveStatus(projectKey);
   }
 
-  private void saveStatus() {
-    cacheStatus.save(project.getKeyWithBranch());
+  private void saveStatus(String projectKey) {
+    cacheStatus.save(projectKey);
     LOG.info("-- Succesfully synchronized project cache");
   }
 
-  private static String getComponentKey(String moduleKey, String filePath) {
-    return moduleKey + ":" + filePath;
-  }
-
-  private void loadData() {
+  private void loadData(String projectKey) {
     Profiler profiler = Profiler.create(Loggers.get(ProjectCacheSynchronizer.class));
 
-    profiler.startInfo("Load project repository");
-    ProjectRepositories projectRepo = projectRepositoryLoader.load(project, properties, null);
+    profiler.startInfo("Load project settings");
+    ProjectSettingsRepo settings = projectSettingsLoader.load(projectKey, null);
     profiler.stopInfo();
 
-    if (projectRepo.lastAnalysisDate() == null) {
+    if (settings.lastAnalysisDate() == null) {
       LOG.debug("No previous analysis found");
       return;
     }
 
+    profiler.startInfo("Load project quality profiles");
+    Collection<QProfile> qProfiles = qualityProfileLoader.load(projectKey, null);
+    profiler.stopInfo();
+
+    Collection<String> profileKeys = getKeys(qProfiles);
+    
+    profiler.startInfo("Load project active rules");
+    activeRulesLoader.load(profileKeys, projectKey);
+    profiler.stopInfo();
+
     profiler.startInfo("Load server issues");
     UserLoginAccumulator consumer = new UserLoginAccumulator();
-    issuesLoader.load(project.getKeyWithBranch(), consumer);
+    issuesLoader.load(projectKey, consumer);
     profiler.stopInfo();
 
     profiler.startInfo("Load user information (" + consumer.loginSet.size() + " users)");
@@ -120,56 +118,15 @@ public class ProjectCacheSynchronizer {
       userRepository.load(login, null);
     }
     profiler.stopInfo("Load user information");
-
-    loadLineHashes(projectRepo.fileDataByModuleAndPath(), profiler);
   }
 
-  private void loadLineHashes(Map<String, Map<String, FileData>> fileDataByModuleAndPath, Profiler profiler) {
-    ExecutorService executor = Executors.newFixedThreadPool(NUM_THREAD);
-    int numFiles = 0;
-
-    for (Map<String, FileData> fileDataByPath : fileDataByModuleAndPath.values()) {
-      numFiles += fileDataByPath.size();
-    }
-    profiler.startInfo("Load line file hashes (" + numFiles + " files)");
-
-    for (Entry<String, Map<String, FileData>> e1 : fileDataByModuleAndPath.entrySet()) {
-      String moduleKey = e1.getKey();
-
-      for (Entry<String, FileData> e2 : e1.getValue().entrySet()) {
-        String filePath = e2.getKey();
-        executor.submit(new LineHashLoadWorker(getComponentKey(moduleKey, filePath)));
-      }
-    }
-
-    executor.shutdown();
-
-    try {
-      boolean done = executor.awaitTermination(30, TimeUnit.MINUTES);
-      if (!done) {
-        executor.shutdownNow();
-        throw new IllegalStateException("Timeout while fetching line hashes");
-      }
-    } catch (InterruptedException e) {
-      executor.shutdownNow();
-      throw new IllegalStateException("Interrupted while fetching line hashes", e);
+  private static Collection<String> getKeys(Collection<QProfile> qProfiles) {
+    List<String> list = new ArrayList<>(qProfiles.size());
+    for (QProfile qp : qProfiles) {
+      list.add(qp.key());
     }
 
-    profiler.stopInfo("Load line file hashes (done)");
-  }
-
-  private class LineHashLoadWorker implements Callable<Void> {
-    private String fileKey;
-
-    LineHashLoadWorker(String fileKey) {
-      this.fileKey = fileKey;
-    }
-
-    @Override
-    public Void call() throws Exception {
-      lineHashesLoader.getLineHashes(fileKey, null);
-      return null;
-    }
+    return list;
   }
 
   private static class UserLoginAccumulator implements Function<ServerIssue, Void> {
index 0f625f72ca0560a85f64146fa636bfbaab918129..0bd67f66261c3692f219b00473faadc770dfde28 100644 (file)
  */
 package org.sonar.batch.cache;
 
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.batch.repository.ProjectRepositoriesFactoryProvider;
+
 import org.sonar.batch.analysis.DefaultAnalysisMode;
-import org.sonar.batch.cache.WSLoader.LoadStrategy;
+import org.sonar.api.CoreProperties;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Map;
+
 import org.sonar.batch.analysis.AnalysisProperties;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.batch.bootstrap.GlobalProperties;
+import org.sonar.batch.repository.ProjectSettingsLoader;
+import org.sonar.batch.repository.DefaultProjectSettingsLoader;
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonar.batch.rule.DefaultActiveRulesLoader;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.sonar.batch.repository.DefaultQualityProfileLoader;
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.batch.repository.user.UserRepositoryLoader;
-import org.sonar.batch.issue.tracking.ServerLineHashesLoader;
 import org.sonar.batch.repository.DefaultProjectRepositoriesLoader;
 import org.sonar.batch.repository.DefaultServerIssuesLoader;
 import org.sonar.batch.repository.ProjectRepositoriesLoader;
 import org.sonar.batch.repository.ServerIssuesLoader;
-import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader;
 import org.sonar.core.platform.ComponentContainer;
 
 public class ProjectSyncContainer extends ComponentContainer {
   private final boolean force;
-  private final AnalysisProperties properties;
+  private final String projectKey;
 
-  public ProjectSyncContainer(ComponentContainer globalContainer, AnalysisProperties analysisProperties, boolean force) {
+  public ProjectSyncContainer(ComponentContainer globalContainer, String projectKey, boolean force) {
     super(globalContainer);
-    this.properties = analysisProperties;
+    this.projectKey = projectKey;
     this.force = force;
   }
 
   @Override
   public void doBeforeStart() {
-    ProjectReactor projectReactor = createProjectReactor();
-    add(projectReactor);
     addComponents();
   }
 
-  private ProjectReactor createProjectReactor() {
-    ProjectDefinition rootProjectDefinition = ProjectDefinition.create();
-    rootProjectDefinition.setProperties(properties.properties());
-    return new ProjectReactor(rootProjectDefinition);
-  }
-
   @Override
   public void doAfterStart() {
-    getComponentByType(ProjectCacheSynchronizer.class).load(force);
+    if (projectKey != null) {
+      getComponentByType(ProjectCacheSynchronizer.class).load(projectKey, force);
+    } else {
+      getComponentByType(NonAssociatedCacheSynchronizer.class).execute(force);
+    }
+  }
+
+  private static DefaultAnalysisMode createIssuesAnalisysMode() {
+    Map<String, String> props = ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES);
+    GlobalProperties globalProps = new GlobalProperties(props);
+    AnalysisProperties analysisProps = new AnalysisProperties(props);
+    return new DefaultAnalysisMode(globalProps, analysisProps);
   }
 
   private void addComponents() {
-    add(new StrategyWSLoaderProvider(LoadStrategy.SERVER_FIRST),
-      properties,
-      DefaultAnalysisMode.class,
-      ProjectCacheSynchronizer.class,
-      UserRepositoryLoader.class);
+    add(new StrategyWSLoaderProvider(LoadStrategy.SERVER_ONLY),
+      projectKey != null ? ProjectCacheSynchronizer.class : NonAssociatedCacheSynchronizer.class,
+      UserRepositoryLoader.class,
+      new ProjectRepositoriesFactoryProvider(projectKey),
+      createIssuesAnalisysMode());
 
     addIfMissing(DefaultProjectCacheStatus.class, ProjectCacheStatus.class);
     addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class);
     addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class);
-    addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class);
+    addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class);
+    addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class);
+    addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class);
   }
-
 }
index 092bab08b0b125241408921458681fafec86a8f0..d6966e9754818b41ad24e6952d9a749876f66ffc 100644 (file)
@@ -51,13 +51,13 @@ public class WSLoader {
     SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY;
   }
 
-  private final LoadStrategy loadStrategy;
+  private final LoadStrategy defautLoadStrategy;
   private ServerStatus serverStatus;
   private final ServerClient client;
   private final PersistentCache cache;
 
   public WSLoader(LoadStrategy strategy, PersistentCache cache, ServerClient client) {
-    this.loadStrategy = strategy;
+    this.defautLoadStrategy = strategy;
     this.serverStatus = UNKNOWN;
     this.cache = cache;
     this.client = client;
@@ -65,19 +65,28 @@ public class WSLoader {
 
   @Nonnull
   public WSLoaderResult<ByteSource> loadSource(String id) {
-    WSLoaderResult<byte[]> byteResult = load(id);
+    WSLoaderResult<byte[]> byteResult = load(id, defautLoadStrategy);
     return new WSLoaderResult<ByteSource>(ByteSource.wrap(byteResult.get()), byteResult.isFromCache());
   }
 
   @Nonnull
   public WSLoaderResult<String> loadString(String id) {
-    WSLoaderResult<byte[]> byteResult = load(id);
+    return loadString(id, defautLoadStrategy);
+  }
+  @Nonnull
+  public WSLoaderResult<String> loadString(String id, WSLoader.LoadStrategy strategy) {
+    WSLoaderResult<byte[]> byteResult = load(id, strategy);
     return new WSLoaderResult<String>(new String(byteResult.get(), StandardCharsets.UTF_8), byteResult.isFromCache());
   }
 
   @Nonnull
   public WSLoaderResult<byte[]> load(String id) {
-    switch (loadStrategy) {
+    return load(id, defautLoadStrategy);
+  }
+  
+  @Nonnull
+  public WSLoaderResult<byte[]> load(String id, WSLoader.LoadStrategy strategy) {
+    switch (strategy) {
       case CACHE_FIRST:
         return loadFromCacheFirst(id, true);
       case CACHE_ONLY:
@@ -91,7 +100,7 @@ public class WSLoader {
   }
 
   public LoadStrategy getStrategy() {
-    return this.loadStrategy;
+    return this.defautLoadStrategy;
   }
 
   private void switchToOffline() {
index 6577835f1cb01dab022ad2f8cbe78d70ef21b8d8..e291a05f15ae7efc363c8c512ca059f1440bafef 100644 (file)
@@ -19,8 +19,9 @@
  */
 package org.sonar.batch.issue.tracking;
 
-import org.sonar.batch.cache.WSLoaderResult;
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
 
+import org.sonar.batch.cache.WSLoaderResult;
 import org.sonar.batch.cache.WSLoader;
 import org.apache.commons.lang.mutable.MutableBoolean;
 
@@ -50,7 +51,7 @@ public class DefaultServerLineHashesLoader implements ServerLineHashesLoader {
     Profiler profiler = Profiler.createIfDebug(Loggers.get(getClass()))
       .addContext("file", fileKey)
       .startDebug("Load line hashes");
-    WSLoaderResult<String> result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey));
+    WSLoaderResult<String> result = wsLoader.loadString("/api/sources/hash?key=" + BatchUtils.encodeForUrl(fileKey), LoadStrategy.CACHE_FIRST);
     try {
       if (fromCache != null) {
         fromCache.setValue(result.isFromCache());
index f202e5526138ffb7c4d5881ca33e5d65879443d0..e0d5cc547c1ec7cf2c4e006975b5e7d5346111f9 100644 (file)
  */
 package org.sonar.batch.issue.tracking;
 
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+
+import org.sonar.batch.repository.ProjectSettingsRepo;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Set;
+
 import javax.annotation.CheckForNull;
+
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.rule.ActiveRule;
@@ -41,7 +47,6 @@ import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.IssueCache;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.batch.protocol.output.BatchReportReader;
 import org.sonar.batch.report.ReportPublisher;
@@ -72,7 +77,7 @@ public class LocalIssueTracking {
   public LocalIssueTracking(BatchComponentCache resourceCache, IssueCache issueCache, IssueTracking tracking,
     ServerLineHashesLoader lastLineHashes, IssueWorkflow workflow, IssueUpdater updater,
     ActiveRules activeRules, ServerIssueRepository serverIssueRepository,
-    ProjectRepositories projectRepositories, ReportPublisher reportPublisher) {
+    ProjectSettingsRepo projectRepositories, ReportPublisher reportPublisher, DefaultAnalysisMode mode) {
     this.componentCache = resourceCache;
     this.issueCache = issueCache;
     this.tracking = tracking;
@@ -84,7 +89,7 @@ public class LocalIssueTracking {
     this.analysisDate = ((Project) resourceCache.getRoot().resource()).getAnalysisDate();
     this.changeContext = IssueChangeContext.createScan(analysisDate);
     this.activeRules = activeRules;
-    this.hasServerAnalysis = projectRepositories.lastAnalysisDate() != null;
+    this.hasServerAnalysis = !mode.isNotAssociated() && projectRepositories.lastAnalysisDate() != null;
   }
 
   public void execute() {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesFactory.java
new file mode 100644 (file)
index 0000000..3861684
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.repository;
+
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.analysis.AnalysisProperties;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+public class DefaultProjectRepositoriesFactory implements ProjectRepositoriesFactory {
+  private static final String LOG_MSG = "Load project repositories";
+  private static final Logger LOG = Loggers.get(DefaultProjectRepositoriesFactory.class);
+  private static final String NON_EXISTING = "non1-existing2-project3-key";
+
+  private final DefaultAnalysisMode analysisMode;
+  private final ProjectRepositoriesLoader loader;
+  private final AnalysisProperties props;
+  private final ProjectReactor projectReactor;
+
+  private ProjectRepositories projectReferentials;
+
+  public DefaultProjectRepositoriesFactory(ProjectReactor projectReactor, DefaultAnalysisMode analysisMode, ProjectRepositoriesLoader loader, AnalysisProperties props) {
+    this.projectReactor = projectReactor;
+    this.analysisMode = analysisMode;
+    this.loader = loader;
+    this.props = props;
+  }
+
+  @Override
+  public ProjectRepositories create() {
+    if (projectReferentials == null) {
+      Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
+      MutableBoolean fromCache = new MutableBoolean();
+      projectReferentials = loader.load(getProjectKey(), getSonarProfile(), fromCache);
+      profiler.stopInfo(fromCache.booleanValue());
+
+      if (analysisMode.isIssues() && projectReferentials.lastAnalysisDate() == null) {
+        LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'.");
+      }
+    }
+    return projectReferentials;
+  }
+
+  private String getProjectKey() {
+    if (analysisMode.isNotAssociated()) {
+      return NON_EXISTING;
+    }
+    return projectReactor.getRoot().getKeyWithBranch();
+  }
+
+  private String getSonarProfile() {
+    String profile = null;
+    if (!analysisMode.isIssues()) {
+      profile = props.property(ModuleQProfiles.SONAR_PROFILE_PROP);
+    }
+    return profile;
+  }
+}
index 58383811367176919d88477c12a0c7641a660e87..a3b38ee4ec54a48d61dd37eb0febe75998c5707f 100644 (file)
@@ -23,12 +23,10 @@ import org.sonar.batch.cache.WSLoaderResult;
 
 import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.cache.WSLoader;
-import org.sonar.batch.analysis.AnalysisProperties;
 
 import javax.annotation.Nullable;
 
 import org.apache.commons.lang.mutable.MutableBoolean;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.utils.MessageException;
@@ -50,13 +48,12 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
   }
 
   @Override
-  public ProjectRepositories load(ProjectDefinition projectDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache) {
-    String projectKey = projectDefinition.getKeyWithBranch();
-    String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKey);
-    if (taskProperties.properties().containsKey(ModuleQProfiles.SONAR_PROFILE_PROP)) {
+  public ProjectRepositories load(String projectKeyWithBranch, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache) {
+    String url = BATCH_PROJECT_URL + "?key=" + BatchUtils.encodeForUrl(projectKeyWithBranch);
+    if (sonarProfile != null) {
       LOG.warn("Ability to set quality profile from command line using '" + ModuleQProfiles.SONAR_PROFILE_PROP
         + "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server.");
-      url += "&profile=" + BatchUtils.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP));
+      url += "&profile=" + BatchUtils.encodeForUrl(sonarProfile);
     }
     url += "&preview=" + analysisMode.isIssues();
 
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectSettingsLoader.java
new file mode 100644 (file)
index 0000000..acb2382
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.repository;
+
+import org.apache.commons.lang.mutable.MutableBoolean;
+
+import javax.annotation.Nullable;
+
+import java.util.Map;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+public class DefaultProjectSettingsLoader implements ProjectSettingsLoader {
+  private ProjectRepositoriesFactory projectRepositoryFactory;
+
+  public DefaultProjectSettingsLoader(ProjectRepositoriesFactory projectRepositoryFactory) {
+    this.projectRepositoryFactory = projectRepositoryFactory;
+  }
+
+  @Override
+  public ProjectSettingsRepo load(String projectKey, @Nullable MutableBoolean fromCache) {
+    ProjectRepositories pr = projectRepositoryFactory.create();
+    return new ProjectSettingsRepo(toTable(pr.settings()), toTable(pr.fileDataByModuleAndPath()), pr.lastAnalysisDate());
+  }
+
+  private static <T, U, V> Table<T, U, V> toTable(Map<T, Map<U, V>> map) {
+    Table<T, U, V> table = HashBasedTable.create();
+
+    for (Map.Entry<T, Map<U, V>> e1 : map.entrySet()) {
+      for (Map.Entry<U, V> e2 : e1.getValue().entrySet()) {
+        table.put(e1.getKey(), e2.getKey(), e2.getValue());
+      }
+    }
+
+    return table;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultQualityProfileLoader.java
new file mode 100644 (file)
index 0000000..ab86309
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.repository;
+
+import javax.annotation.Nullable;
+
+import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.batch.protocol.input.QProfile;
+
+import java.util.Collection;
+
+public class DefaultQualityProfileLoader implements QualityProfileLoader {
+
+  private ProjectRepositoriesFactory projectRepositoriesFactory;
+
+  public DefaultQualityProfileLoader(ProjectRepositoriesFactory projectRepositoriesFactory) {
+    this.projectRepositoriesFactory = projectRepositoriesFactory;
+  }
+
+  @Override
+  public Collection<QProfile> load(@Nullable String projectKey, @Nullable String sonarProfile) {
+    ProjectRepositories pr = projectRepositoriesFactory.create();
+    validate(pr.qProfiles());
+    return pr.qProfiles();
+  }
+
+  private static void validate(Collection<QProfile> profiles) {
+    if (profiles == null || profiles.isEmpty()) {
+      throw new IllegalStateException("No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis.");
+    }
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactory.java
new file mode 100644 (file)
index 0000000..13ce299
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.repository;
+
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+public interface ProjectRepositoriesFactory {
+
+  ProjectRepositories create();
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesFactoryProvider.java
new file mode 100644 (file)
index 0000000..609d7a7
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.repository;
+
+import org.picocontainer.injectors.ProviderAdapter;
+
+public class ProjectRepositoriesFactoryProvider extends ProviderAdapter {
+
+  private final String projectKey;
+  private SyncProjectRepositoriesFactory factory;
+
+  public ProjectRepositoriesFactoryProvider(String projectKey) {
+    this.projectKey = projectKey;
+    this.factory = null;
+  }
+
+  public ProjectRepositoriesFactory provide(ProjectRepositoriesLoader loader) {
+    if (factory == null) {
+      factory = new SyncProjectRepositoriesFactory(projectKey, loader);
+    }
+
+    return factory;
+  }
+}
index 444af1a3b629953a3f690b6579fb99ed12212172..d066b488940e795e7c65d5729b35bc36a4a68e9a 100644 (file)
  */
 package org.sonar.batch.repository;
 
-import org.sonar.batch.analysis.AnalysisProperties;
 
 import javax.annotation.Nullable;
 
 import org.apache.commons.lang.mutable.MutableBoolean;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.batch.protocol.input.ProjectRepositories;
 
 public interface ProjectRepositoriesLoader {
 
-  ProjectRepositories load(ProjectDefinition projectDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache);
+  ProjectRepositories load(String projectKeyWithBranch, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache);
 
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectRepositoriesProvider.java
deleted file mode 100644 (file)
index b2c86b9..0000000
+++ /dev/null
@@ -1,53 +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.repository;
-
-import org.sonar.batch.analysis.AnalysisProperties;
-
-import org.apache.commons.lang.mutable.MutableBoolean;
-import org.picocontainer.injectors.ProviderAdapter;
-import org.sonar.api.batch.AnalysisMode;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.api.utils.log.Profiler;
-import org.sonar.batch.protocol.input.ProjectRepositories;
-
-public class ProjectRepositoriesProvider extends ProviderAdapter {
-
-  private static final String LOG_MSG = "Load project repositories";
-  private static final Logger LOG = Loggers.get(ProjectRepositoriesProvider.class);
-
-  private ProjectRepositories projectReferentials;
-
-  public ProjectRepositories provide(ProjectRepositoriesLoader loader, ProjectReactor reactor, AnalysisProperties taskProps, AnalysisMode analysisMode) {
-    if (projectReferentials == null) {
-      Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
-      MutableBoolean fromCache = new MutableBoolean();
-      projectReferentials = loader.load(reactor.getRoot(), taskProps, fromCache);
-      profiler.stopInfo(fromCache.booleanValue());
-
-      if (analysisMode.isIssues() && projectReferentials.lastAnalysisDate() == null) {
-        LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'.");
-      }
-    }
-    return projectReferentials;
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsLoader.java
new file mode 100644 (file)
index 0000000..dc0efbc
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.repository;
+
+import org.apache.commons.lang.mutable.MutableBoolean;
+
+import javax.annotation.Nullable;
+
+public interface ProjectSettingsLoader {
+  ProjectSettingsRepo load(String projectKey, @Nullable MutableBoolean fromCache);
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsProvider.java
new file mode 100644 (file)
index 0000000..5fa0db4
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.repository;
+
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+import javax.annotation.Nullable;
+
+import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.protocol.input.FileData;
+import com.google.common.collect.Table;
+import com.google.common.collect.ImmutableTable;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.picocontainer.injectors.ProviderAdapter;
+
+public class ProjectSettingsProvider extends ProviderAdapter {
+  private static final Logger LOG = Loggers.get(ProjectSettingsProvider.class);
+  private ProjectSettingsRepo settings = null;
+
+  public ProjectSettingsRepo provide(@Nullable ProjectSettingsLoader loader, ProjectReactor projectReactor, DefaultAnalysisMode mode) {
+    if (settings == null) {
+      if (mode.isNotAssociated()) {
+        settings = createNonAssociatedProjectSettings();
+      } else {
+        MutableBoolean fromCache = new MutableBoolean();
+        settings = loader.load(projectReactor.getRoot().getKeyWithBranch(), fromCache);
+        checkProject(mode);
+      }
+    }
+
+    return settings;
+  }
+
+  private void checkProject(DefaultAnalysisMode mode) {
+    if (mode.isIssues() && settings.lastAnalysisDate() == null) {
+      LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'.");
+    }
+  }
+
+  private static ProjectSettingsRepo createNonAssociatedProjectSettings() {
+    Table<String, String, String> emptySettings = ImmutableTable.of();
+    Table<String, String, FileData> emptyFileData = ImmutableTable.of();
+    return new ProjectSettingsRepo(emptySettings, emptyFileData, null);
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectSettingsRepo.java
new file mode 100644 (file)
index 0000000..1e53de4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.repository;
+
+import com.google.common.collect.Table;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import org.sonar.batch.protocol.input.FileData;
+
+import java.util.Date;
+import java.util.Map;
+
+public class ProjectSettingsRepo {
+  private Table<String, String, String> settingsByModule = null;
+  private Table<String, String, FileData> fileDataByModuleAndPath = null;
+  private Date lastAnalysisDate;
+
+  public ProjectSettingsRepo(Table<String, String, String> settingsByModule, Table<String, String, FileData> fileDataByModuleAndPath,
+    @Nullable Date lastAnalysisDate) {
+    super();
+    this.settingsByModule = settingsByModule;
+    this.fileDataByModuleAndPath = fileDataByModuleAndPath;
+    this.lastAnalysisDate = lastAnalysisDate;
+  }
+
+  public Map<String, FileData> fileDataByPath(String moduleKey) {
+    return fileDataByModuleAndPath.row(moduleKey);
+  }
+
+  public Table<String, String, FileData> fileDataByModuleAndPath() {
+    return fileDataByModuleAndPath;
+  }
+
+  public Map<String, String> settings(String moduleKey) {
+    return settingsByModule.row(moduleKey);
+  }
+
+  @CheckForNull
+  public FileData fileData(String projectKey, String path) {
+    return fileDataByModuleAndPath.get(projectKey, path);
+  }
+
+  @CheckForNull
+  public Date lastAnalysisDate() {
+    return lastAnalysisDate;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileLoader.java
new file mode 100644 (file)
index 0000000..1d41398
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.repository;
+
+import javax.annotation.Nullable;
+
+import org.sonar.batch.protocol.input.QProfile;
+
+import java.util.Collection;
+
+public interface QualityProfileLoader {
+  Collection<QProfile> load(@Nullable String projectKey, @Nullable String sonarProfile);
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/repository/QualityProfileProvider.java
new file mode 100644 (file)
index 0000000..6f40ef0
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.repository;
+
+import org.sonar.batch.protocol.input.QProfile;
+
+import java.util.Collection;
+
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.batch.analysis.AnalysisProperties;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.picocontainer.injectors.ProviderAdapter;
+
+public class QualityProfileProvider extends ProviderAdapter {
+  private ModuleQProfiles profiles = null;
+
+  public ModuleQProfiles provide(ProjectReactor projectReactor, QualityProfileLoader loader, AnalysisProperties props, AnalysisMode mode) {
+    if (this.profiles == null) {
+      String profile = null;
+      if (!mode.isIssues()) {
+        profile = props.property(ModuleQProfiles.SONAR_PROFILE_PROP);
+      }
+      Collection<QProfile> qps = loader.load(projectReactor.getRoot().getKeyWithBranch(), profile);
+      profiles = new ModuleQProfiles(qps);
+    }
+
+    return profiles;
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java b/sonar-batch/src/main/java/org/sonar/batch/repository/SyncProjectRepositoriesFactory.java
new file mode 100644 (file)
index 0000000..f01cce9
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.repository;
+
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+public class SyncProjectRepositoriesFactory implements ProjectRepositoriesFactory {
+  private static final String LOG_MSG = "Load project repositories";
+  private static final Logger LOG = Loggers.get(SyncProjectRepositoriesFactory.class);
+  private static final String NON_EXISTING = "non1-existing2-project3-key";
+
+  private final ProjectRepositoriesLoader loader;
+  private final String projectKey;
+
+  private ProjectRepositories projectRepositories;
+
+  public SyncProjectRepositoriesFactory(@Nullable String projectKey, ProjectRepositoriesLoader loader) {
+    this.projectKey = projectKey;
+    this.loader = loader;
+  }
+
+  @Override
+  public ProjectRepositories create() {
+    if (projectRepositories == null) {
+      projectRepositories = newInstance();
+    }
+
+    return projectRepositories;
+  }
+
+  public ProjectRepositories newInstance() {
+    if (projectRepositories == null) {
+      Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
+      MutableBoolean fromCache = new MutableBoolean();
+      projectRepositories = loader.load(getProjectKey(projectKey), null, fromCache);
+      profiler.stopInfo(fromCache.booleanValue());
+
+      if (projectRepositories.lastAnalysisDate() == null) {
+        LOG.warn("No analysis has been found on the server for this project. All issues will be marked as 'new'.");
+      }
+    }
+    return projectRepositories;
+  }
+
+  private static String getProjectKey(@Nullable String projectKey) {
+    if (projectKey == null) {
+      return NON_EXISTING;
+    }
+    return projectKey;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesLoader.java
new file mode 100644 (file)
index 0000000..bf2ff6d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.rule;
+
+import org.sonar.batch.protocol.input.ActiveRule;
+
+import java.util.Collection;
+
+public interface ActiveRulesLoader {
+  Collection<ActiveRule> load(Collection<String> qualityProfileKeys, String projectKey);
+}
index 5bb0a4a86bd74d80d480a27df9f4ad8cd21cd750..ee2529cadff76d96c1739468e9980f394587a910 100644 (file)
  */
 package org.sonar.batch.rule;
 
+import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.picocontainer.injectors.ProviderAdapter;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
 import org.sonar.api.batch.rule.internal.NewActiveRule;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.batch.protocol.input.ActiveRule;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map.Entry;
 
 /**
@@ -37,16 +40,16 @@ public class ActiveRulesProvider extends ProviderAdapter {
 
   private ActiveRules singleton = null;
 
-  public ActiveRules provide(ProjectRepositories ref) {
+  public ActiveRules provide(ActiveRulesLoader ref, ModuleQProfiles qProfiles, ProjectReactor projectReactor) {
     if (singleton == null) {
-      singleton = load(ref);
+      singleton = load(ref, qProfiles, projectReactor);
     }
     return singleton;
   }
 
-  private static ActiveRules load(ProjectRepositories ref) {
+  private static ActiveRules load(ActiveRulesLoader loader, ModuleQProfiles qProfiles, ProjectReactor projectReactor) {
     ActiveRulesBuilder builder = new ActiveRulesBuilder();
-    for (ActiveRule activeRule : ref.activeRules()) {
+    for (ActiveRule activeRule : loader.load(getKeys(qProfiles), projectReactor.getRoot().getKeyWithBranch())) {
       NewActiveRule newActiveRule = builder.create(RuleKey.of(activeRule.repositoryKey(), activeRule.ruleKey()));
       newActiveRule.setName(activeRule.name());
       newActiveRule.setSeverity(activeRule.severity());
@@ -63,4 +66,14 @@ public class ActiveRulesProvider extends ProviderAdapter {
     }
     return builder.build();
   }
+
+  private static Collection<String> getKeys(ModuleQProfiles qProfiles) {
+    List<String> keys = new ArrayList<>(qProfiles.findAll().size());
+
+    for (QProfile qp : qProfiles.findAll()) {
+      keys.add(qp.getKey());
+    }
+
+    return keys;
+  }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/rule/DefaultActiveRulesLoader.java
new file mode 100644 (file)
index 0000000..05e5884
--- /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.rule;
+
+import org.sonar.batch.repository.ProjectRepositoriesFactory;
+
+import org.sonar.batch.protocol.input.ActiveRule;
+
+import java.util.Collection;
+
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+public class DefaultActiveRulesLoader implements ActiveRulesLoader {
+  private final ProjectRepositoriesFactory projectRepositoriesFactory;
+
+  public DefaultActiveRulesLoader(ProjectRepositoriesFactory projectRepositoriesFactory) {
+    this.projectRepositoriesFactory = projectRepositoriesFactory;
+  }
+
+  @Override
+  public Collection<ActiveRule> load(Collection<String> qualityProfileKeys, String projectKey) {
+    ProjectRepositories pr = projectRepositoriesFactory.create();
+    return pr.activeRules();
+  }
+
+}
index f7474cf181d00a17ccf2260953243ec9320a5f10..3ac70d831507fff00b67d34ff9140445f9b4ef73 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.batch.rule;
 
 import com.google.common.collect.ImmutableMap;
 import org.sonar.api.batch.BatchSide;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 
 import javax.annotation.CheckForNull;
 
@@ -37,10 +36,10 @@ public class ModuleQProfiles {
   public static final String SONAR_PROFILE_PROP = "sonar.profile";
   private final Map<String, QProfile> byLanguage;
 
-  public ModuleQProfiles(ProjectRepositories ref) {
+  public ModuleQProfiles(Collection<org.sonar.batch.protocol.input.QProfile> profiles) {
     ImmutableMap.Builder<String, QProfile> builder = ImmutableMap.builder();
 
-    for (org.sonar.batch.protocol.input.QProfile qProfile : ref.qProfiles()) {
+    for (org.sonar.batch.protocol.input.QProfile qProfile : profiles) {
       builder.put(qProfile.language(),
         new QProfile().setKey(qProfile.key()).setName(qProfile.name()).setLanguage(qProfile.language()).setRulesUpdatedAt(qProfile.rulesUpdatedAt()));
     }
index 039da7d57e0e3dc4b6292a56498dab3098ef7e14..89c5e8bcc171ecfcec9213411429809b534c4bb4 100644 (file)
@@ -148,7 +148,6 @@ public class ModuleScanContainer extends ComponentContainer {
       CoverageExclusions.class,
 
     // rules
-      ModuleQProfiles.class,
       new RulesProfileProvider(),
       QProfileSensor.class,
       CheckFactory.class,
index f8b770f59dd3a04c4d7afa2b0dbd71062c4337c0..00692d53cfae184ec49b07bbaee841ef0d61dc2c 100644 (file)
@@ -19,8 +19,9 @@
  */
 package org.sonar.batch.scan;
 
-import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.repository.ProjectSettingsRepo;
 
+import org.sonar.batch.analysis.DefaultAnalysisMode;
 import com.google.common.collect.Lists;
 
 import java.util.List;
@@ -30,20 +31,19 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.MessageException;
 import org.sonar.batch.bootstrap.GlobalSettings;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 
 /**
  * @since 2.12
  */
 public class ModuleSettings extends Settings {
 
-  private final ProjectRepositories projectReferentials;
+  private final ProjectSettingsRepo projectSettingsRepo;
   private DefaultAnalysisMode analysisMode;
 
-  public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectRepositories projectReferentials,
+  public ModuleSettings(GlobalSettings batchSettings, ProjectDefinition moduleDefinition, ProjectSettingsRepo projectSettingsRepo,
     DefaultAnalysisMode analysisMode) {
     super(batchSettings.getDefinitions());
-    this.projectReferentials = projectReferentials;
+    this.projectSettingsRepo = projectSettingsRepo;
     this.analysisMode = analysisMode;
     getEncryption().setPathToSecretKey(batchSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
 
@@ -58,13 +58,13 @@ public class ModuleSettings extends Settings {
 
   private void addProjectProperties(ProjectDefinition moduleDefinition, GlobalSettings batchSettings) {
     addProperties(batchSettings.getProperties());
-    addProperties(projectReferentials.settings(moduleDefinition.getKeyWithBranch()));
+    addProperties(projectSettingsRepo.settings(moduleDefinition.getKeyWithBranch()));
   }
 
   private void addBuildProperties(ProjectDefinition project) {
     List<ProjectDefinition> orderedProjects = getTopDownParentProjects(project);
     for (ProjectDefinition p : orderedProjects) {
-      addProperties(p.getProperties());
+      addProperties(p.properties());
     }
   }
 
index 8f42e7bc3667d7dbf53fd6297eabeea82362845b..6266bf44e6c6a9bf0b691d48cfc73cdb8f1d3bd0 100644 (file)
@@ -55,7 +55,6 @@ public class ProjectLock implements Startable {
       if (lockFile == null) {
         failAlreadyInProgress(null);
       }
-      
     } catch (OverlappingFileLockException e) {
       failAlreadyInProgress(e);
     } catch (IOException e) {
index 96d7bad1c5c476945b39eb6f79e8393a99c475d6..81b54609351128b8ff4db6c51741ec3c665fefb8 100644 (file)
@@ -19,8 +19,9 @@
  */
 package org.sonar.batch.scan;
 
-import org.apache.commons.lang.ArrayUtils;
+import org.sonar.api.batch.AnalysisMode;
 
+import org.apache.commons.lang.ArrayUtils;
 import org.sonar.batch.analysis.AnalysisProperties;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
@@ -108,12 +109,16 @@ public class ProjectReactorBuilder {
    */
   private static final List<String> NON_HERITED_PROPERTIES_FOR_CHILD = Lists.newArrayList(PROPERTY_PROJECT_BASEDIR, CoreProperties.WORKING_DIRECTORY, PROPERTY_MODULES,
     CoreProperties.PROJECT_DESCRIPTION_PROPERTY);
+  
+  private static final String NON_ASSOCIATED_PROJECT_KEY = "project";
 
-  private AnalysisProperties taskProps;
+  private final AnalysisProperties taskProps;
+  private final AnalysisMode analysisMode;
   private File rootProjectWorkDir;
 
-  public ProjectReactorBuilder(AnalysisProperties props) {
+  public ProjectReactorBuilder(AnalysisProperties props, AnalysisMode analysisMode) {
     this.taskProps = props;
+    this.analysisMode = analysisMode;
   }
 
   public ProjectReactor execute() {
@@ -160,8 +165,16 @@ public class ProjectReactorBuilder {
       extractPropertiesByModule(propertiesByModuleId, moduleId, currentModuleProperties);
     }
   }
-
+  
+  private static void prepareNonAssociatedProject(Map<String, String> props, AnalysisMode mode) {
+    if(mode.isIssues() && !props.containsKey(CoreProperties.PROJECT_KEY_PROPERTY)) {
+      props.put(CoreProperties.PROJECT_KEY_PROPERTY, NON_ASSOCIATED_PROJECT_KEY);
+    }
+  }
+  
   protected ProjectDefinition defineRootProject(Map<String, String> rootProperties, @Nullable ProjectDefinition parent) {
+    prepareNonAssociatedProject(rootProperties, analysisMode);
+    
     if (rootProperties.containsKey(PROPERTY_MODULES)) {
       checkMandatoryProperties(rootProperties, MANDATORY_PROPERTIES_FOR_MULTIMODULE_PROJECT);
     } else {
index b53e457ab7cd864645e3386c9ac8fcef587dd798..a856b44b8947660a467d79d8a3828d880a632b16 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.api.config.Settings;
-import org.sonar.api.utils.SonarException;
 import org.sonar.core.component.ComponentKeys;
 
 /**
@@ -58,7 +57,7 @@ public class ProjectReactorValidator {
     validateBranch(validationMessages, branch);
 
     if (!validationMessages.isEmpty()) {
-      throw new SonarException("Validation of project reactor failed:\n  o " + Joiner.on("\n  o ").join(validationMessages));
+      throw new IllegalStateException("Validation of project reactor failed:\n  o " + Joiner.on("\n  o ").join(validationMessages));
     }
   }
 
index b00888779cfea8e5467d32418619a7ac7eaba3d3..d6f58f0852a7021b419fd07a46d0ba379c71c659 100644 (file)
  */
 package org.sonar.batch.scan;
 
+import org.sonar.batch.repository.DefaultProjectRepositoriesFactory;
+
+import org.sonar.batch.repository.QualityProfileProvider;
+import org.sonar.batch.repository.DefaultQualityProfileLoader;
+import org.sonar.batch.repository.QualityProfileLoader;
+import org.sonar.batch.repository.ProjectSettingsLoader;
+import org.sonar.batch.repository.DefaultProjectSettingsLoader;
+import org.sonar.batch.repository.ProjectSettingsProvider;
+import org.sonar.batch.rule.DefaultActiveRulesLoader;
+import org.sonar.batch.rule.ActiveRulesLoader;
 import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.analysis.AnalysisWSLoaderProvider;
 import org.sonar.batch.analysis.AnalysisTempFolderProvider;
@@ -69,7 +79,6 @@ import org.sonar.batch.report.MetadataPublisher;
 import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.report.SourcePublisher;
 import org.sonar.batch.report.TestExecutionAndCoveragePublisher;
-import org.sonar.batch.repository.ProjectRepositoriesProvider;
 import org.sonar.batch.repository.language.DefaultLanguagesRepository;
 import org.sonar.batch.rule.ActiveRulesProvider;
 import org.sonar.batch.scan.filesystem.InputPathCache;
@@ -116,6 +125,7 @@ public class ProjectScanContainer extends ComponentContainer {
       props,
       DefaultAnalysisMode.class,
       ProjectReactorBuilder.class,
+      DefaultProjectRepositoriesFactory.class,
       new MutableProjectReactorProvider(),
       new ImmutableProjectReactorProvider(),
       ProjectBuildersExecutor.class,
@@ -126,7 +136,6 @@ public class ProjectScanContainer extends ComponentContainer {
       DefaultProjectTree.class,
       ProjectExclusions.class,
       ProjectReactorValidator.class,
-      new ProjectRepositoriesProvider(),
       new AnalysisWSLoaderProvider(),
       CodeColorizers.class,
       MetricProvider.class,
@@ -136,6 +145,7 @@ public class ProjectScanContainer extends ComponentContainer {
       Caches.class,
       BatchComponentCache.class,
       DefaultIssueCallback.class,
+      new ProjectSettingsProvider(),
 
       // temp
       new AnalysisTempFolderProvider(),
@@ -146,6 +156,7 @@ public class ProjectScanContainer extends ComponentContainer {
 
       // rules
       new ActiveRulesProvider(),
+      new QualityProfileProvider(),
 
       // issues
       IssueUpdater.class,
@@ -153,8 +164,8 @@ public class ProjectScanContainer extends ComponentContainer {
       IssueWorkflow.class,
       IssueCache.class,
       DefaultProjectIssues.class,
-      LocalIssueTracking.class,
       ServerIssueRepository.class,
+      LocalIssueTracking.class,
 
       // metrics
       DefaultMetricFinder.class,
@@ -190,9 +201,16 @@ public class ProjectScanContainer extends ComponentContainer {
       ScanTaskObservers.class,
       UserRepositoryLoader.class);
 
-    addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class);
     addIfMissing(DefaultServerIssuesLoader.class, ServerIssuesLoader.class);
     addIfMissing(DefaultServerLineHashesLoader.class, ServerLineHashesLoader.class);
+    addIfMissing(DefaultActiveRulesLoader.class, ActiveRulesLoader.class);
+    addIfMissing(DefaultQualityProfileLoader.class, QualityProfileLoader.class);
+    addIfMissing(DefaultProjectRepositoriesLoader.class, ProjectRepositoriesLoader.class);
+    addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class);
+  }
+
+  private boolean isProjectAssociated() {
+    return !getComponentByType(DefaultAnalysisMode.class).isNotAssociated();
   }
 
   private void addBatchExtensions() {
index 7cd20223d20b96551ffce5ac5937d134d6993725..df9b51351004ecdc28f3f93fdf27223a2d1d80a4 100644 (file)
@@ -19,8 +19,9 @@
  */
 package org.sonar.batch.scan;
 
-import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.repository.ProjectSettingsRepo;
 
+import org.sonar.batch.analysis.DefaultAnalysisMode;
 import com.google.common.collect.ImmutableMap;
 
 import java.util.Map;
@@ -32,7 +33,6 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.utils.MessageException;
 import org.sonar.batch.bootstrap.DroppedPropertyChecker;
 import org.sonar.batch.bootstrap.GlobalSettings;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 
 public class ProjectSettings extends Settings {
 
@@ -45,11 +45,11 @@ public class ProjectSettings extends Settings {
     );
 
   private final GlobalSettings globalSettings;
-  private final ProjectRepositories projectRepositories;
+  private final ProjectSettingsRepo projectRepositories;
   private final DefaultAnalysisMode mode;
 
   public ProjectSettings(ProjectReactor reactor, GlobalSettings globalSettings, PropertyDefinitions propertyDefinitions,
-    ProjectRepositories projectRepositories, DefaultAnalysisMode mode) {
+    ProjectSettingsRepo projectRepositories, DefaultAnalysisMode mode) {
     super(propertyDefinitions);
     this.mode = mode;
     getEncryption().setPathToSecretKey(globalSettings.getString(CoreProperties.ENCRYPTION_SECRET_KEY_PATH));
index 36131c29331678528fbe8b2cf277980023f0c0a3..51232ec5701fee576d5d7351761379ca8c472e73 100644 (file)
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.batch.repository.ProjectSettingsRepo;
+
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.batch.protocol.input.FileData;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 
 class StatusDetection {
 
-  private final ProjectRepositories projectReferentials;
+  private final ProjectSettingsRepo projectSettings;
 
-  StatusDetection(ProjectRepositories projectReferentials) {
-    this.projectReferentials = projectReferentials;
+  StatusDetection(ProjectSettingsRepo projectSettings) {
+    this.projectSettings = projectSettings;
   }
 
   InputFile.Status status(String projectKey, String relativePath, String hash) {
-    FileData fileDataPerPath = projectReferentials.fileData(projectKey, relativePath);
+    FileData fileDataPerPath = projectSettings.fileData(projectKey, relativePath);
     if (fileDataPerPath == null) {
       return InputFile.Status.ADDED;
     }
index 643f915cafcaff261dec20c6a2b9a4f14d0b53d3..f372b171df230e821167dd5f6099c25acb3d007d 100644 (file)
  */
 package org.sonar.batch.scan.filesystem;
 
+import org.sonar.batch.repository.ProjectSettingsRepo;
+
 import org.sonar.api.batch.BatchSide;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 
 @BatchSide
 public class StatusDetectionFactory {
 
-  private final ProjectRepositories projectReferentials;
+  private final ProjectSettingsRepo projectReferentials;
 
-  public StatusDetectionFactory(ProjectRepositories projectReferentials) {
+  public StatusDetectionFactory(ProjectSettingsRepo projectReferentials) {
     this.projectReferentials = projectReferentials;
   }
 
index 62144aa09d05803fa30ee1c97895d511dc78a3fb..7d24836072dced076d126cbaae8ac867a2897e49 100644 (file)
  */
 package org.sonar.batch.scm;
 
+import org.sonar.batch.repository.ProjectSettingsRepo;
+
 import java.util.LinkedList;
 import java.util.List;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
@@ -33,7 +36,6 @@ import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.SensorDescriptor;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.input.FileData;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.filesystem.InputPathCache;
 
@@ -44,16 +46,16 @@ public final class ScmSensor implements Sensor {
   private final ProjectDefinition projectDefinition;
   private final ScmConfiguration configuration;
   private final FileSystem fs;
-  private final ProjectRepositories projectReferentials;
+  private final ProjectSettingsRepo projectSettings;
   private final BatchComponentCache resourceCache;
   private final ReportPublisher publishReportJob;
 
   public ScmSensor(ProjectDefinition projectDefinition, ScmConfiguration configuration,
-    ProjectRepositories projectReferentials, FileSystem fs, InputPathCache inputPathCache, BatchComponentCache resourceCache,
+    ProjectSettingsRepo projectSettings, FileSystem fs, InputPathCache inputPathCache, BatchComponentCache resourceCache,
     ReportPublisher publishReportJob) {
     this.projectDefinition = projectDefinition;
     this.configuration = configuration;
-    this.projectReferentials = projectReferentials;
+    this.projectSettings = projectSettings;
     this.fs = fs;
     this.resourceCache = resourceCache;
     this.publishReportJob = publishReportJob;
@@ -95,7 +97,7 @@ public final class ScmSensor implements Sensor {
       if (configuration.forceReloadAll()) {
         addIfNotEmpty(filesToBlame, f);
       } else {
-        FileData fileData = projectReferentials.fileData(projectDefinition.getKeyWithBranch(), f.relativePath());
+        FileData fileData = projectSettings.fileData(projectDefinition.getKeyWithBranch(), f.relativePath());
         if (f.status() != Status.SAME || fileData == null || fileData.needBlame()) {
           addIfNotEmpty(filesToBlame, f);
         }
index 838edd04d39e9b4832f2b522765501ad10db2490..4239ca1c024edad6a0a084df3e55c98e07feb175 100644 (file)
@@ -25,7 +25,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import org.sonar.home.cache.PersistentCacheLoader;
 
-import org.junit.internal.runners.statements.ExpectException;
 import org.junit.rules.ExpectedException;
 
 import java.io.IOException;
@@ -58,6 +57,7 @@ public class DefaultProjectCacheStatusTest {
   public void setUp() {
     cache = new PersistentCache(tmp.getRoot().toPath(), Long.MAX_VALUE, mock(Logger.class), null);
     client = mock(ServerClient.class);
+    when(client.getServerVersion()).thenReturn("5.2");
     when(client.getURL()).thenReturn("localhost");
     cacheStatus = new DefaultProjectCacheStatus(cache, client);
   }
@@ -84,6 +84,17 @@ public class DefaultProjectCacheStatusTest {
     cacheStatus.save(PROJ_KEY);
   }
   
+  @Test
+  public void useServerVersionAsKey() {
+    cacheStatus.save(PROJ_KEY);
+    assertThat(cacheStatus.getSyncStatus(PROJ_KEY)).isNotNull();
+    assertThat(age(cacheStatus.getSyncStatus(PROJ_KEY))).isLessThan(2000);
+    
+    when(client.getServerVersion()).thenReturn("5.1");
+    
+    assertThat(cacheStatus.getSyncStatus(PROJ_KEY)).isNull();
+  }
+  
   @Test
   public void errorStatus() throws IOException {
     cache = mock(PersistentCache.class);
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cache/NonAssociatedCacheSynchronizerTest.java
new file mode 100644 (file)
index 0000000..d7b3bbd
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.cache;
+
+import static org.mockito.Mockito.when;
+import org.sonar.batch.protocol.input.ActiveRule;
+import com.google.common.collect.ImmutableList;
+import org.sonar.batch.protocol.input.QProfile;
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.mockito.Mockito.mock;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import org.mockito.MockitoAnnotations;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonar.batch.repository.QualityProfileLoader;
+
+public class NonAssociatedCacheSynchronizerTest {
+  private NonAssociatedCacheSynchronizer synchronizer;
+
+  @Mock
+  private QualityProfileLoader qualityProfileLoader;
+  @Mock
+  private ActiveRulesLoader activeRulesLoader;
+  @Mock
+  private ProjectCacheStatus cacheStatus;
+
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+
+    QProfile pf = new QProfile("profile", "profile", "lang", new Date(1000));
+    ActiveRule ar = mock(ActiveRule.class);
+
+    when(qualityProfileLoader.load(null, null)).thenReturn(ImmutableList.of(pf));
+    when(activeRulesLoader.load(ImmutableList.of("profile"), null)).thenReturn(ImmutableList.of(ar));
+
+    synchronizer = new NonAssociatedCacheSynchronizer(qualityProfileLoader, activeRulesLoader, cacheStatus);
+  }
+
+  @Test
+  public void dont_sync_if_exists() {
+    when(cacheStatus.getSyncStatus(null)).thenReturn(new Date());
+    synchronizer.execute(false);
+    verifyNoMoreInteractions(qualityProfileLoader, activeRulesLoader);
+  }
+
+  @Test
+  public void always_sync_if_force() {
+    when(cacheStatus.getSyncStatus(null)).thenReturn(new Date());
+    synchronizer.execute(true);
+    checkSync();
+  }
+
+  @Test
+  public void sync_if_doesnt_exist() {
+    synchronizer.execute(false);
+    checkSync();
+  }
+
+  private void checkSync() {
+    verify(cacheStatus).getSyncStatus(null);
+    verify(cacheStatus).save(null);
+    verify(qualityProfileLoader).load(null, null);
+    verify(activeRulesLoader).load(ImmutableList.of("profile"), null);
+
+    verifyNoMoreInteractions(qualityProfileLoader, activeRulesLoader);
+  }
+}
index 9e4966a5687a4c5c9966eb501159196122c4a7b8..3f9c4af0e7717f7099ba69f340cae935e6fa2c80 100644 (file)
  */
 package org.sonar.batch.cache;
 
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
 import org.sonar.batch.bootstrap.GlobalProperties;
 import org.sonar.batch.cache.PersistentCacheProvider;
 
-import java.nio.file.Paths;
+import java.io.File;
 import java.util.Collections;
 
 import org.junit.Before;
@@ -30,6 +32,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 import org.junit.Test;
 
 public class PersistentCacheProviderTest {
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+  
   private PersistentCacheProvider provider = null;
   private GlobalProperties props = null;
 
@@ -51,7 +56,8 @@ public class PersistentCacheProviderTest {
 
   @Test
   public void test_home() {
-    props.properties().put("sonar.userHome", "myhome");
-    assertThat(provider.provide(props).getBaseDirectory()).isEqualTo(Paths.get("myhome/ws_cache"));
+    File f = temp.getRoot();
+    props.properties().put("sonar.userHome", f.getAbsolutePath());
+    assertThat(provider.provide(props).getBaseDirectory()).isEqualTo(f.toPath().resolve("ws_cache"));
   }
 }
index c36603145e3dc65934fad11530b9606a6080b7ed..2621af3347677a42e464ddc90361121d87dffe76 100644 (file)
 package org.sonar.batch.cache;
 
 import static org.mockito.Mockito.when;
+
+import org.sonar.batch.repository.ProjectRepositoriesFactory;
+
+import org.sonar.batch.repository.DefaultProjectRepositoriesFactory;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.sonar.batch.repository.ProjectSettingsRepo;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.QProfile;
+import org.apache.commons.lang.mutable.MutableBoolean;
+import org.sonar.batch.repository.DefaultProjectSettingsLoader;
+import org.sonar.batch.rule.DefaultActiveRulesLoader;
+import org.sonar.batch.repository.DefaultQualityProfileLoader;
+import org.sonar.batch.repository.ProjectSettingsLoader;
+import org.sonar.batch.rule.ActiveRulesLoader;
+import org.sonar.batch.repository.QualityProfileLoader;
 import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.analysis.AnalysisProperties;
 import org.sonar.batch.protocol.input.ProjectRepositories;
-import org.apache.commons.lang.mutable.MutableBoolean;
-import org.sonar.batch.issue.tracking.DefaultServerLineHashesLoader;
 import org.sonar.batch.repository.DefaultServerIssuesLoader;
 import org.sonar.batch.repository.DefaultProjectRepositoriesLoader;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
@@ -36,6 +52,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.Date;
 import java.util.HashMap;
 
+import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Matchers.anyString;
@@ -47,7 +64,6 @@ import org.junit.Before;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Mock;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.batch.issue.tracking.ServerLineHashesLoader;
 import org.sonar.batch.repository.ProjectRepositoriesLoader;
 import org.sonar.batch.repository.ServerIssuesLoader;
 import org.sonar.batch.repository.user.UserRepositoryLoader;
@@ -55,8 +71,10 @@ import org.sonar.batch.repository.user.UserRepositoryLoader;
 public class ProjectCacheSynchronizerTest {
   private static final String BATCH_PROJECT = "/batch/project?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin&preview=true";
   private static final String ISSUES = "/batch/issues?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin";
-  private static final String LINE_HASHES1 = "/api/sources/hash?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin%3Asrc%2Ftest%2Fjava%2Forg%2Fsonar%2Fplugins%2Fscm%2Fgit%2FJGitBlameCommandTest.java";
-  private static final String LINE_HASHES2 = "/api/sources/hash?key=org.codehaus.sonar-plugins%3Asonar-scm-git-plugin%3Asrc%2Fmain%2Fjava%2Forg%2Fsonar%2Fplugins%2Fscm%2Fgit%2FGitScmProvider.java";
+  private static final String PROJECT_KEY = "org.codehaus.sonar-plugins:sonar-scm-git-plugin";
+
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
 
   @Mock
   private ProjectDefinition project;
@@ -73,10 +91,10 @@ public class ProjectCacheSynchronizerTest {
 
   private ProjectRepositoriesLoader projectRepositoryLoader;
   private ServerIssuesLoader issuesLoader;
-  private ServerLineHashesLoader lineHashesLoader;
   private UserRepositoryLoader userRepositoryLoader;
-
-  private ProjectCacheSynchronizer sync;
+  private QualityProfileLoader qualityProfileLoader;
+  private ActiveRulesLoader activeRulesLoader;
+  private ProjectSettingsLoader projectSettingsLoader;
 
   @Before
   public void setUp() throws IOException {
@@ -84,62 +102,108 @@ public class ProjectCacheSynchronizerTest {
 
     String batchProject = getResourceAsString("batch_project.json");
     ByteSource issues = getResourceAsByteSource("batch_issues.protobuf");
-    String lineHashes2 = getResourceAsString("api_sources_hash_GitScmProvider.text");
-    String lineHashes1 = getResourceAsString("api_sources_hash_JGitBlameCommand.text");
 
     when(ws.loadString(BATCH_PROJECT)).thenReturn(new WSLoaderResult<>(batchProject, false));
     when(ws.loadSource(ISSUES)).thenReturn(new WSLoaderResult<>(issues, false));
-    when(ws.loadString(LINE_HASHES1)).thenReturn(new WSLoaderResult<>(lineHashes1, false));
-    when(ws.loadString(LINE_HASHES2)).thenReturn(new WSLoaderResult<>(lineHashes2, false));
 
     when(analysisMode.isIssues()).thenReturn(true);
-    when(project.getKeyWithBranch()).thenReturn("org.codehaus.sonar-plugins:sonar-scm-git-plugin");
-    when(projectReactor.getRoot()).thenReturn(project);
     when(properties.properties()).thenReturn(new HashMap<String, String>());
+  }
+
+  private ProjectCacheSynchronizer create(ProjectRepositories projectRepositories) {
+    if (projectRepositories == null) {
+      projectRepositoryLoader = new DefaultProjectRepositoriesLoader(ws, analysisMode);
+    } else {
+      projectRepositoryLoader = mock(ProjectRepositoriesLoader.class);
+      when(projectRepositoryLoader.load(anyString(), anyString(), any(MutableBoolean.class))).thenReturn(projectRepositories);
+    }
+
+    ProjectReactor reactor = mock(ProjectReactor.class);
+    ProjectDefinition root = mock(ProjectDefinition.class);
+    when(root.getKeyWithBranch()).thenReturn(PROJECT_KEY);
+    when(reactor.getRoot()).thenReturn(root);
+
+    ProjectRepositoriesFactory projectRepositoriesFactory = new DefaultProjectRepositoriesFactory(reactor, analysisMode, projectRepositoryLoader, properties);
 
-    projectRepositoryLoader = new DefaultProjectRepositoriesLoader(ws, analysisMode);
     issuesLoader = new DefaultServerIssuesLoader(ws);
-    lineHashesLoader = new DefaultServerLineHashesLoader(ws);
     userRepositoryLoader = new UserRepositoryLoader(ws);
+    qualityProfileLoader = new DefaultQualityProfileLoader(projectRepositoriesFactory);
+    activeRulesLoader = new DefaultActiveRulesLoader(projectRepositoriesFactory);
+    projectSettingsLoader = new DefaultProjectSettingsLoader(projectRepositoriesFactory);
+
+    return new ProjectCacheSynchronizer(qualityProfileLoader, projectSettingsLoader, activeRulesLoader, issuesLoader, userRepositoryLoader, cacheStatus);
+  }
+
+  private ProjectCacheSynchronizer createMockedLoaders(Date lastAnalysisDate) {
+    issuesLoader = mock(DefaultServerIssuesLoader.class);
+    userRepositoryLoader = mock(UserRepositoryLoader.class);
+    qualityProfileLoader = mock(DefaultQualityProfileLoader.class);
+    activeRulesLoader = mock(DefaultActiveRulesLoader.class);
+    projectSettingsLoader = mock(DefaultProjectSettingsLoader.class);
+
+    QProfile pf = new QProfile("profile", "profile", "lang", new Date(1000));
+    ActiveRule ar = mock(ActiveRule.class);
+    ProjectSettingsRepo repo = mock(ProjectSettingsRepo.class);
 
-    sync = new ProjectCacheSynchronizer(projectReactor, projectRepositoryLoader, properties, issuesLoader, lineHashesLoader, userRepositoryLoader,
-      cacheStatus);
+    when(qualityProfileLoader.load(PROJECT_KEY, null)).thenReturn(ImmutableList.of(pf));
+    when(activeRulesLoader.load(ImmutableList.of("profile"), PROJECT_KEY)).thenReturn(ImmutableList.of(ar));
+    when(repo.lastAnalysisDate()).thenReturn(lastAnalysisDate);
+    when(projectSettingsLoader.load(anyString(), any(MutableBoolean.class))).thenReturn(repo);
+
+    return new ProjectCacheSynchronizer(qualityProfileLoader, projectSettingsLoader, activeRulesLoader, issuesLoader, userRepositoryLoader, cacheStatus);
   }
 
   @Test
   public void testSync() {
-    sync.load(false);
+    ProjectCacheSynchronizer sync = create(null);
+
+    sync.load(PROJECT_KEY, false);
 
     verify(ws).loadString(BATCH_PROJECT);
     verify(ws).loadSource(ISSUES);
-    verify(ws).loadString(LINE_HASHES1);
-    verify(ws).loadString(LINE_HASHES2);
     verifyNoMoreInteractions(ws);
 
     verify(cacheStatus).save(anyString());
   }
 
+  @Test
+  public void testLoadersUsage() {
+    ProjectCacheSynchronizer synchronizer = createMockedLoaders(new Date());
+    synchronizer.load(PROJECT_KEY, false);
+
+    verify(issuesLoader).load(eq(PROJECT_KEY), any(Function.class));
+    verify(qualityProfileLoader).load(PROJECT_KEY, null);
+    verify(activeRulesLoader).load(ImmutableList.of("profile"), PROJECT_KEY);
+    verify(projectSettingsLoader).load(eq(PROJECT_KEY), any(MutableBoolean.class));
+
+    verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectSettingsLoader);
+  }
+
+  @Test
+  public void testLoadersUsage_NoLastAnalysis() {
+    ProjectCacheSynchronizer synchronizer = createMockedLoaders(null);
+    synchronizer.load(PROJECT_KEY, false);
+
+    verify(projectSettingsLoader).load(eq(PROJECT_KEY), any(MutableBoolean.class));
+
+    verifyNoMoreInteractions(issuesLoader, userRepositoryLoader, qualityProfileLoader, activeRulesLoader, projectSettingsLoader);
+  }
+
   @Test
   public void testSyncNoLastAnalysis() {
-    projectRepositoryLoader = mock(DefaultProjectRepositoriesLoader.class);
     ProjectRepositories mockedProjectRepositories = mock(ProjectRepositories.class);
     when(mockedProjectRepositories.lastAnalysisDate()).thenReturn(null);
-    when(projectRepositoryLoader.load(any(ProjectDefinition.class), any(AnalysisProperties.class), any(MutableBoolean.class))).thenReturn(mockedProjectRepositories);
-
-    sync = new ProjectCacheSynchronizer(projectReactor, projectRepositoryLoader, properties, issuesLoader, lineHashesLoader, userRepositoryLoader,
-      cacheStatus);
-    sync.load(true);
 
-    verify(cacheStatus).save("org.codehaus.sonar-plugins:sonar-scm-git-plugin");
+    ProjectCacheSynchronizer sync = create(mockedProjectRepositories);
+    sync.load(PROJECT_KEY, true);
+    verify(cacheStatus).save(PROJECT_KEY);
   }
 
   @Test
   public void testDontSyncIfNotForce() {
-    when(cacheStatus.getSyncStatus("org.codehaus.sonar-plugins:sonar-scm-git-plugin")).thenReturn(new Date());
-
-    ProjectCacheSynchronizer sync = new ProjectCacheSynchronizer(projectReactor, projectRepositoryLoader, properties, issuesLoader, lineHashesLoader, userRepositoryLoader,
-      cacheStatus);
-    sync.load(false);
+    when(cacheStatus.getSyncStatus(PROJECT_KEY)).thenReturn(new Date());
+    ProjectCacheSynchronizer sync = create(null);
+    sync.load(PROJECT_KEY, false);
 
     verifyNoMoreInteractions(ws);
   }
index b6d132ae8b2f6513ba6609bfa94482be3ad33671..15833a06ad534efd9e48dac82dd731972da14b96 100644 (file)
  */
 package org.sonar.batch.cache;
 
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
 import org.sonar.batch.bootstrap.GlobalProperties;
 import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.home.cache.PersistentCache;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.batch.analysis.AnalysisProperties;
 
 import java.util.HashMap;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
 
 import static org.mockito.Mockito.mock;
 import org.sonar.core.platform.ComponentContainer;
@@ -47,24 +44,11 @@ public class ProjectSyncContainerTest {
     return parent;
   }
 
-  public AnalysisProperties createProjectProperties() {
-    Map<String, String> properties = new HashMap<>();
-    properties.put("sonar.branch", "branch");
-    properties.put("sonar.projectKey", "my:project");
-    properties.put("sonar.projectName", "My project");
-    properties.put("sonar.projectVersion", "1.0");
-    properties.put("sonar.sources", ".");
-    properties.put("sonar.projectBaseDir", ".");
-    return new AnalysisProperties(properties);
-  }
-
   @Test
-  public void testProjectKeyWithBranch() {
-    ProjectSyncContainer container = new ProjectSyncContainer(createParentContainer(), createProjectProperties(), true);
+  public void testProjectRepository() {
+    ProjectSyncContainer container = new ProjectSyncContainer(createParentContainer(), "my:project", true);
     container.doBeforeStart();
     container.getPicoContainer().start();
-    
-    ProjectReactor projectReactor = container.getComponentByType(ProjectReactor.class);
-    assertThat(projectReactor.getRoot().getKeyWithBranch()).isEqualTo("my:project:branch");
+    container.getComponentByType(ProjectRepositories.class);
   }
 }
index 1ba67f296f179a8f6b4fa45414ecd3716235116a..9a3c25f6a342c43af5a031e2fc692247a5b5ae5f 100644 (file)
@@ -19,8 +19,8 @@
  */
 package org.sonar.batch.issue.tracking;
 
+import org.sonar.batch.cache.WSLoader.LoadStrategy;
 import org.sonar.batch.cache.WSLoaderResult;
-
 import org.sonar.batch.cache.WSLoader;
 import org.apache.commons.lang.mutable.MutableBoolean;
 import org.junit.Before;
@@ -32,6 +32,8 @@ import org.sonar.api.utils.HttpDownloader;
 import java.net.URI;
 import java.net.URISyntaxException;
 
+import static org.mockito.Matchers.any;
+
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -50,19 +52,19 @@ public class DefaultServerLineHashesLoaderTest {
   @Test
   public void should_download_source_from_ws_if_preview_mode() {
     WSLoader wsLoader = mock(WSLoader.class);
-    when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true));
+    when(wsLoader.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true));
 
     ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(wsLoader);
 
     String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Bar.c", null);
     assertThat(hashes).containsOnly("ae12", "", "43fb");
-    verify(wsLoader).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FBar.c");
+    verify(wsLoader).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FBar.c", LoadStrategy.CACHE_FIRST);
   }
 
   @Test
   public void should_download_source_with_space_from_ws_if_preview_mode() {
     WSLoader server = mock(WSLoader.class);
-    when(server.loadString(anyString())).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true));
+    when(server.loadString(anyString(), any(LoadStrategy.class))).thenReturn(new WSLoaderResult<>("ae12\n\n43fb", true));
 
     ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
 
@@ -70,13 +72,13 @@ public class DefaultServerLineHashesLoaderTest {
     String[] hashes = lastSnapshots.getLineHashes("myproject:org/foo/Foo Bar.c", fromCache);
     assertThat(fromCache.booleanValue()).isTrue();
     assertThat(hashes).containsOnly("ae12", "", "43fb");
-    verify(server).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FFoo+Bar.c");
+    verify(server).loadString("/api/sources/hash?key=myproject%3Aorg%2Ffoo%2FFoo+Bar.c", LoadStrategy.CACHE_FIRST);
   }
 
   @Test
   public void should_fail_to_download_source_from_ws() throws URISyntaxException {
     WSLoader server = mock(WSLoader.class);
-    when(server.loadString(anyString())).thenThrow(new HttpDownloader.HttpException(new URI(""), 500));
+    when(server.loadString(anyString(), any(LoadStrategy.class))).thenThrow(new HttpDownloader.HttpException(new URI(""), 500));
 
     ServerLineHashesLoader lastSnapshots = new DefaultServerLineHashesLoader(server);
 
index b14d6ac3c88f657bb44cdab42a37cf658d6525e4..9f3e1c4f9c236e306be9914c0fdd7a4d4abc0cad 100644 (file)
  */
 package org.sonar.batch.mediumtest;
 
-import org.sonar.batch.analysis.AnalysisProperties;
-
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.mutable.MutableBoolean;
 
 import javax.annotation.Nullable;
 
 import org.sonar.batch.cache.ProjectCacheStatus;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonarqube.ws.Rules.ListResponse.Rule;
 import org.sonar.batch.bootstrapper.IssueListener;
 import org.sonar.api.server.rule.RulesDefinition.Repository;
 import org.sonar.api.server.rule.RulesDefinition;
 import org.sonar.batch.rule.RulesLoader;
 import com.google.common.base.Function;
-import com.google.common.io.Files;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -75,12 +74,41 @@ public class BatchMediumTester {
 
   public static final String MEDIUM_TEST_ENABLED = "sonar.mediumTest.enabled";
   private Batch batch;
+  private static Path workingDir = null;
+  private static Path globalWorkingDir = null;
+  
+  private static void createWorkingDirs() throws IOException {
+    destroyWorkingDirs();
+    
+    workingDir = java.nio.file.Files.createTempDirectory("mediumtest-working-dir");
+    globalWorkingDir = java.nio.file.Files.createTempDirectory("mediumtest-global-working-dir");
+  }
+  
+  private static void destroyWorkingDirs() throws IOException {
+    if(workingDir != null) {
+      FileUtils.deleteDirectory(workingDir.toFile());
+      workingDir = null;
+    }
+    
+    if(globalWorkingDir != null) {
+      FileUtils.deleteDirectory(globalWorkingDir.toFile());
+      globalWorkingDir = null;
+    }
+    
+  }
 
   public static BatchMediumTesterBuilder builder() {
+    try {
+      createWorkingDirs();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    
     BatchMediumTesterBuilder builder = new BatchMediumTesterBuilder().registerCoreMetrics();
     builder.bootstrapProperties.put(MEDIUM_TEST_ENABLED, "true");
     builder.bootstrapProperties.put(ReportPublisher.KEEP_REPORT_PROP_KEY, "true");
-    builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, Files.createTempDir().getAbsolutePath());
+    builder.bootstrapProperties.put(CoreProperties.WORKING_DIRECTORY, workingDir.toString());
+    builder.bootstrapProperties.put(CoreProperties.GLOBAL_WORKING_DIRECTORY, globalWorkingDir.toString());
     return builder;
   }
 
@@ -93,12 +121,18 @@ public class BatchMediumTester {
     private final Map<String, String> bootstrapProperties = new HashMap<>();
     private final FakeRulesLoader rulesLoader = new FakeRulesLoader();
     private final FakeProjectCacheStatus projectCacheStatus = new FakeProjectCacheStatus();
+    private boolean associated = true;
     private LogOutput logOutput = null;
 
     public BatchMediumTester build() {
       return new BatchMediumTester(this);
     }
 
+    public BatchMediumTesterBuilder setAssociated(boolean associated) {
+      this.associated = associated;
+      return this;
+    }
+
     public BatchMediumTesterBuilder setLogOutput(LogOutput logOutput) {
       this.logOutput = logOutput;
       return this;
@@ -210,6 +244,11 @@ public class BatchMediumTester {
 
   public void stop() {
     batch.stop();
+    try {
+      destroyWorkingDirs();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
   }
 
   public void syncProject(String projectKey) {
@@ -217,21 +256,24 @@ public class BatchMediumTester {
   }
 
   private BatchMediumTester(BatchMediumTesterBuilder builder) {
-    batch = Batch.builder()
+    Batch.Builder batchBuilder = Batch.builder()
       .setEnableLoggingConfiguration(true)
       .addComponents(
         new EnvironmentInformation("mediumTest", "1.0"),
         builder.pluginInstaller,
         builder.globalRefProvider,
-        builder.projectRefProvider,
-        builder.serverIssues,
-        builder.serverLineHashes,
         builder.rulesLoader,
         builder.projectCacheStatus,
+        builder.projectRefProvider,
         new DefaultDebtModel())
       .setBootstrapProperties(builder.bootstrapProperties)
-      .setLogOutput(builder.logOutput)
-      .build();
+      .setLogOutput(builder.logOutput);
+
+    if (builder.associated) {
+      batchBuilder.addComponents(
+        builder.serverIssues);
+    }
+    batch = batchBuilder.build();
   }
 
   public TaskBuilder newTask() {
@@ -343,7 +385,7 @@ public class BatchMediumTester {
     private ProjectRepositories ref = new ProjectRepositories();
 
     @Override
-    public ProjectRepositories load(ProjectDefinition projDefinition, AnalysisProperties taskProperties, @Nullable MutableBoolean fromCache) {
+    public ProjectRepositories load(String projectKey, @Nullable String sonarProfile, @Nullable MutableBoolean fromCache) {
       return ref;
     }
 
@@ -385,7 +427,6 @@ public class BatchMediumTester {
       }
       return true;
     }
-
   }
 
   private static class FakeProjectCacheStatus implements ProjectCacheStatus {
index 3c5d27499264c2a1dc523b89749d084e20d975a5..8216eea1323415062e55c6c6ee5add81b2a7cf65 100644 (file)
  */
 package org.sonar.batch.mediumtest.cache;
 
-import org.sonar.batch.protocol.input.FileData;
-
-import org.junit.Test;
 import com.google.common.collect.ImmutableMap;
 import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.CoreProperties;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.input.FileData;
 import org.sonar.xoo.XooPlugin;
 import org.sonar.xoo.rule.XooRulesDefinition;
 
 import java.util.Date;
 
 public class CacheSyncTest {
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
 
-  public BatchMediumTester tester;
+  private BatchMediumTester tester;
 
   @After
   public void stop() {
@@ -47,23 +50,57 @@ public class CacheSyncTest {
   @Test
   public void testSyncFirstTime() {
     FileData file1 = new FileData("hash", true);
-    String[] hashes = new String[] {
-      "line1", "line2"
-    };
 
     tester = BatchMediumTester.builder()
       .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
       .registerPlugin("xoo", new XooPlugin())
       .addRules(new XooRulesDefinition())
+      .addQProfile("lang", "name")
       .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo"))
       .setPreviousAnalysisDate(new Date())
       .addFileData("test-project", "file1", file1)
-      .mockLineHashes("test-project:file1", hashes)
       .build();
 
     tester.start();
     tester.syncProject("test-project");
+  }
+  
+  @Test
+  public void testNonAssociated() {
+    FileData file1 = new FileData("hash", true);
+
+    tester = BatchMediumTester.builder()
+      .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+      .registerPlugin("xoo", new XooPlugin())
+      .addRules(new XooRulesDefinition())
+      .addQProfile("lang", "name")
+      .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo"))
+      .setPreviousAnalysisDate(new Date())
+      .addFileData("test-project", "file1", file1)
+      .build();
 
+    tester.start();
+    tester.syncProject(null);
+  }
+
+  @Test
+  public void testNoQProfile() {
+    FileData file1 = new FileData("hash", true);
+
+    tester = BatchMediumTester.builder()
+      .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES))
+      .registerPlugin("xoo", new XooPlugin())
+      .addRules(new XooRulesDefinition())
+      .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", "my/internal/key", "xoo"))
+      .setPreviousAnalysisDate(new Date())
+      .addFileData("test-project", "file1", file1)
+      .build();
+
+    tester.start();
+
+    exception.expect(IllegalStateException.class);
+    exception.expectMessage("No quality");
+    tester.syncProject("test-project");
   }
 
 }
diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issuesmode/NonAssociatedProject.java
new file mode 100644 (file)
index 0000000..6b2ae4c
--- /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.mediumtest.issuesmode;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.batch.mediumtest.BatchMediumTester;
+import org.sonar.batch.mediumtest.TaskResult;
+import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.rule.XooRulesDefinition;
+
+import java.io.File;
+import java.io.IOException;
+
+public class NonAssociatedProject {
+  @org.junit.Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @org.junit.Rule
+  public LogTester logTester = new LogTester();
+
+  public BatchMediumTester tester;
+
+  @Before
+  public void prepare() throws IOException {
+    tester = BatchMediumTester.builder()
+      .bootstrapProperties(ImmutableMap.of(
+        CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ISSUES,
+        CoreProperties.GLOBAL_WORKING_DIRECTORY, temp.newFolder().getAbsolutePath()))
+      .registerPlugin("xoo", new XooPlugin())
+      .addQProfile("xoo", "Sonar Way")
+      .addRules(new XooRulesDefinition())
+      .addRule("manual:MyManualIssue", "manual", "MyManualIssue", "My manual issue")
+      .addRule("manual:MyManualIssueDup", "manual", "MyManualIssue", "My manual issue")
+      .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo"))
+      .activateRule(new ActiveRule("xoo", "OneIssueOnDirPerFile", null, "OneIssueOnDirPerFile", "MAJOR", null, "xoo"))
+      .activateRule(new ActiveRule("xoo", "OneIssuePerModule", null, "OneIssuePerModule", "MAJOR", null, "xoo"))
+      .activateRule(new ActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null))
+      .setAssociated(false)
+      .build();
+    tester.start();
+  }
+
+  @After
+  public void stop() {
+    tester.stop();
+  }
+
+  private File copyProject(String path) throws Exception {
+    File projectDir = temp.newFolder();
+    File originalProjectDir = new File(IssueModeAndReportsMediumTest.class.getResource(path).toURI());
+    FileUtils.copyDirectory(originalProjectDir, projectDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter(".sonar")));
+    return projectDir;
+  }
+
+  @Test
+  public void testNonAssociated() throws Exception {
+    File projectDir = copyProject("/mediumtest/xoo/multi-modules-sample-not-associated");
+
+    TaskResult result = tester
+      .newScanTask(new File(projectDir, "sonar-project.properties"))
+      .start();
+
+  }
+}
index 5e59d70beda88030eac134e59486f8eb220ab040..3d2b9d18937baa774cc4f112522d334997145f4d 100644 (file)
@@ -23,10 +23,8 @@ import org.sonar.batch.cache.WSLoaderResult;
 
 import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.cache.WSLoader;
-import org.sonar.batch.analysis.AnalysisProperties;
 import org.apache.commons.lang.mutable.MutableBoolean;
 import org.apache.commons.io.IOUtils;
-import com.google.common.collect.Maps;
 
 import java.io.IOException;
 import java.util.Date;
@@ -40,7 +38,6 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.utils.MessageException;
 import org.sonar.batch.protocol.input.ProjectRepositories;
 import org.sonar.batch.protocol.input.QProfile;
-import org.sonar.batch.rule.ModuleQProfiles;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -56,7 +53,6 @@ public class DefaultProjectRepositoriesLoaderTest {
   private WSLoader wsLoader;
   private DefaultAnalysisMode analysisMode;
   private ProjectDefinition project;
-  private AnalysisProperties taskProperties;
 
   @Before
   public void prepare() {
@@ -65,7 +61,6 @@ public class DefaultProjectRepositoriesLoaderTest {
     loader = new DefaultProjectRepositoriesLoader(wsLoader, analysisMode);
     loader = spy(loader);
     when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>("{}", true));
-    taskProperties = new AnalysisProperties(Maps.<String, String>newHashMap(), "");
   }
 
   @Test
@@ -73,11 +68,11 @@ public class DefaultProjectRepositoriesLoaderTest {
     addQualityProfile();
     project = ProjectDefinition.create().setKey("foo");
     when(analysisMode.isIssues()).thenReturn(false);
-    loader.load(project, taskProperties, null);
+    loader.load(project.getKeyWithBranch(), null, null);
     verify(wsLoader).loadString("/batch/project?key=foo&preview=false");
 
     when(analysisMode.isIssues()).thenReturn(true);
-    loader.load(project, taskProperties, null);
+    loader.load(project.getKeyWithBranch(), null, null);
     verify(wsLoader).loadString("/batch/project?key=foo&preview=true");
   }
 
@@ -88,7 +83,7 @@ public class DefaultProjectRepositoriesLoaderTest {
     when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>(response, true));
     project = ProjectDefinition.create().setKey("foo");
     MutableBoolean fromCache = new MutableBoolean();
-    ProjectRepositories projectRepo = loader.load(project, taskProperties, fromCache);
+    ProjectRepositories projectRepo = loader.load(project.getKeyWithBranch(), null, fromCache);
 
     assertThat(fromCache.booleanValue()).isTrue();
     assertThat(projectRepo.activeRules().size()).isEqualTo(221);
@@ -100,7 +95,7 @@ public class DefaultProjectRepositoriesLoaderTest {
   public void passAndEncodeProjectKeyParameter() {
     addQualityProfile();
     project = ProjectDefinition.create().setKey("foo bàr");
-    loader.load(project, taskProperties, null);
+    loader.load(project.getKeyWithBranch(), null, null);
     verify(wsLoader).loadString("/batch/project?key=foo+b%C3%A0r&preview=false");
   }
 
@@ -108,8 +103,7 @@ public class DefaultProjectRepositoriesLoaderTest {
   public void passAndEncodeProfileParameter() {
     addQualityProfile();
     project = ProjectDefinition.create().setKey("foo");
-    taskProperties.properties().put(ModuleQProfiles.SONAR_PROFILE_PROP, "my-profile#2");
-    loader.load(project, taskProperties, null);
+    loader.load(project.getKeyWithBranch(), "my-profile#2", null);
     verify(wsLoader).loadString("/batch/project?key=foo&profile=my-profile%232&preview=false");
   }
 
@@ -121,7 +115,7 @@ public class DefaultProjectRepositoriesLoaderTest {
     project = ProjectDefinition.create().setKey("foo");
     when(wsLoader.loadString(anyString())).thenReturn(new WSLoaderResult<>(new ProjectRepositories().toJson(), true));
 
-    loader.load(project, taskProperties, null);
+    loader.load(project.getKeyWithBranch(), null, null);
   }
 
   private void addQualityProfile() {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectSettingsLoaderTest.java
new file mode 100644 (file)
index 0000000..9d0388f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.repository;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.sonar.batch.protocol.input.FileData;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class DefaultProjectSettingsLoaderTest {
+  private DefaultProjectSettingsLoader loader;
+  private DefaultProjectRepositoriesFactory factory;
+  private ProjectRepositories projectRepositories;
+
+  private FileData f1;
+  private FileData f2;
+
+  @Before
+  public void setUp() {
+    createProjectRepo();
+    factory = mock(DefaultProjectRepositoriesFactory.class);
+    when(factory.create()).thenReturn(projectRepositories);
+    loader = new DefaultProjectSettingsLoader(factory);
+  }
+
+  private void createProjectRepo() {
+    projectRepositories = new ProjectRepositories();
+    projectRepositories.setLastAnalysisDate(new Date(1000));
+
+    f1 = new FileData("hash1", true);
+    f2 = new FileData("hash2", true);
+    projectRepositories.addFileData("module1", "file1", f1);
+    projectRepositories.addFileData("module1", "file2", f2);
+
+    projectRepositories.addSettings("module1", ImmutableMap.of("key", "value"));
+  }
+
+  @Test
+  public void test() {
+    ProjectSettingsRepo loaded = loader.load("project", null);
+
+    assertThat(loaded.fileData("module1", "file1")).isEqualTo(f1);
+    assertThat(loaded.fileData("module1", "file2")).isEqualTo(f2);
+    assertThat(loaded.settings("module1")).isEqualTo(ImmutableMap.of("key", "value"));
+    assertThat(loaded.lastAnalysisDate()).isEqualTo(new Date(1000));
+
+    verify(factory).create();
+    verifyNoMoreInteractions(factory);
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultQualityProfileLoaderTest.java
new file mode 100644 (file)
index 0000000..06de84f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.repository;
+
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.batch.protocol.input.QProfile;
+
+import java.util.Collection;
+import java.util.Date;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultQualityProfileLoaderTest {
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
+
+  private DefaultQualityProfileLoader qpLoader;
+  private DefaultProjectRepositoriesFactory factory;
+  private ProjectRepositories projectRepositories;
+
+  @Before
+  public void setUp() {
+    projectRepositories = new ProjectRepositories();
+    projectRepositories.addQProfile(new QProfile("profile", "name", "lang", new Date()));
+
+    factory = mock(DefaultProjectRepositoriesFactory.class);
+    when(factory.create()).thenReturn(projectRepositories);
+    qpLoader = new DefaultQualityProfileLoader(factory);
+  }
+
+  @Test
+  public void test() {
+    Collection<QProfile> loaded = qpLoader.load("project", null);
+
+    assertThat(loaded).hasSize(1);
+    assertThat(loaded.iterator().next().key()).isEqualTo("profile");
+    verify(factory).create();
+    verifyNoMoreInteractions(factory);
+  }
+
+  @Test
+  public void testNoProfile() {
+    projectRepositories = new ProjectRepositories();
+    when(factory.create()).thenReturn(projectRepositories);
+
+    exception.expect(IllegalStateException.class);
+    exception.expectMessage("No quality profiles");
+
+    qpLoader.load("project", null);
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/QualityProfileProviderTest.java
new file mode 100644 (file)
index 0000000..357f0bc
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.repository;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.sonar.batch.protocol.input.QProfile;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import static org.mockito.Mockito.verify;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.junit.Test;
+import org.sonar.batch.analysis.AnalysisProperties;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.batch.AnalysisMode;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.mockito.Mock;
+import org.junit.Before;
+
+public class QualityProfileProviderTest {
+  private QualityProfileProvider qualityProfileProvider;
+
+  @Mock
+  private QualityProfileLoader loader;
+  @Mock
+  private ProjectReactor projectReactor;
+  @Mock
+  private AnalysisMode mode;
+  @Mock
+  private AnalysisProperties props;
+
+  private Collection<QProfile> response;
+
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    qualityProfileProvider = new QualityProfileProvider();
+
+    ProjectDefinition root = mock(ProjectDefinition.class);
+    when(root.getKeyWithBranch()).thenReturn("project");
+    when(projectReactor.getRoot()).thenReturn(root);
+
+    response = new ArrayList<QProfile>(1);
+    response.add(new QProfile("profile", "name", "lang", new Date()));
+  }
+
+  @Test
+  public void testProvide() {
+    when(loader.load("project", null)).thenReturn(response);
+    ModuleQProfiles qps = qualityProfileProvider.provide(projectReactor, loader, props, mode);
+    assertResponse(qps);
+
+    verify(loader).load("project", null);
+  }
+
+  @Test
+  public void testProfileProp() {
+    when(loader.load("project", "custom")).thenReturn(response);
+    when(props.property(ModuleQProfiles.SONAR_PROFILE_PROP)).thenReturn("custom");
+
+    ModuleQProfiles qps = qualityProfileProvider.provide(projectReactor, loader, props, mode);
+    assertResponse(qps);
+
+    verify(loader).load("project", "custom");
+  }
+
+  private void assertResponse(ModuleQProfiles qps) {
+    assertThat(qps.findAll()).hasSize(1);
+    assertThat(qps.findAll()).extracting("key").containsExactly("profile");
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/DefaultActiveRulesLoaderTest.java
new file mode 100644 (file)
index 0000000..7c3f2e1
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.rule;
+
+import org.sonar.batch.repository.DefaultProjectRepositoriesFactory;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.batch.protocol.input.ActiveRule;
+import org.junit.Test;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+import java.util.Collection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import org.junit.Before;
+
+public class DefaultActiveRulesLoaderTest {
+  private DefaultActiveRulesLoader loader;
+  private DefaultProjectRepositoriesFactory factory;
+  private ProjectRepositories projectRepositories;
+
+  private ActiveRule response;
+
+  @Before
+  public void setUp() {
+    response = mock(ActiveRule.class);
+    when(response.ruleKey()).thenReturn("rule");
+
+    projectRepositories = new ProjectRepositories();
+    projectRepositories.addActiveRule(response);
+
+    factory = mock(DefaultProjectRepositoriesFactory.class);
+    when(factory.create()).thenReturn(projectRepositories);
+    loader = new DefaultActiveRulesLoader(factory);
+  }
+
+  @Test
+  public void test() {
+    Collection<String> profiles = ImmutableList.of("profile1");
+    Collection<ActiveRule> activeRules = loader.load(profiles, "project");
+
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.iterator().next().ruleKey()).isEqualTo("rule");
+
+    verify(factory).create();
+    verifyNoMoreInteractions(factory);
+  }
+
+}
index c8a5f50601800045ae1b8a28131ac64e40aac6cb..b68eee07f32b5c898a6d9a54bfd3edb6943d2f7a 100644 (file)
  */
 package org.sonar.batch.scan;
 
-import org.sonar.batch.analysis.DefaultAnalysisMode;
+import com.google.common.collect.HashBasedTable;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -29,10 +31,13 @@ import org.junit.rules.ExpectedException;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.PropertyDefinitions;
 import org.sonar.api.utils.MessageException;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.GlobalSettings;
-import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.batch.protocol.input.FileData;
+import org.sonar.batch.repository.ProjectSettingsRepo;
 
 import java.util.List;
+import java.util.Map;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
@@ -43,15 +48,23 @@ public class ModuleSettingsTest {
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
-  ProjectRepositories projectRef;
   private DefaultAnalysisMode mode;
 
   @Before
   public void before() {
-    projectRef = new ProjectRepositories();
     mode = mock(DefaultAnalysisMode.class);
   }
 
+  private ProjectSettingsRepo createSettings(String module, Map<String, String> settingsMap) {
+    Table<String, String, FileData> fileData = ImmutableTable.of();
+    Table<String, String, String> settings = HashBasedTable.create();
+
+    for (Map.Entry<String, String> e : settingsMap.entrySet()) {
+      settings.put(module, e.getKey(), e.getValue());
+    }
+    return new ProjectSettingsRepo(settings, fileData, null);
+  }
+
   @Test
   public void testOrderedProjects() {
     ProjectDefinition grandParent = ProjectDefinition.create();
@@ -74,11 +87,12 @@ public class ModuleSettingsTest {
       "overridding", "batch",
       "on-batch", "true"
       ));
-    projectRef.addSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module"));
+
+    ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("on-module", "true", "overridding", "module"));
 
     ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
 
-    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projectRef, mode);
+    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode);
 
     assertThat(moduleSettings.getString("overridding")).isEqualTo("module");
     assertThat(moduleSettings.getString("on-batch")).isEqualTo("true");
@@ -93,11 +107,12 @@ public class ModuleSettingsTest {
     when(batchSettings.getProperties()).thenReturn(ImmutableMap.of(
       "sonar.foo.secured", "bar"
       ));
-    projectRef.addSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2"));
+
+    ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2"));
 
     ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
 
-    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projectRef, mode);
+    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode);
 
     assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
     assertThat(moduleSettings.getString("sonar.foo.secured")).isEqualTo("bar");
@@ -110,13 +125,14 @@ public class ModuleSettingsTest {
     when(batchSettings.getProperties()).thenReturn(ImmutableMap.of(
       "sonar.foo.secured", "bar"
       ));
-    projectRef.addSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2"));
+
+    ProjectSettingsRepo projSettingsRepo = createSettings("struts-core", ImmutableMap.of("sonar.foo.license.secured", "bar2"));
 
     when(mode.isIssues()).thenReturn(true);
 
     ProjectDefinition module = ProjectDefinition.create().setKey("struts-core");
 
-    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projectRef, mode);
+    ModuleSettings moduleSettings = new ModuleSettings(batchSettings, module, projSettingsRepo, mode);
 
     assertThat(moduleSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
 
index 5ba26864ec4b46a94c188352b00b3d4e62a4c8de..75900b811ded03312faef4c5c079a9d15f97cfc6 100644 (file)
@@ -20,7 +20,8 @@
 package org.sonar.batch.scan;
 
 import org.apache.commons.lang.StringUtils;
-
+import org.junit.Before;
+import org.sonar.api.batch.AnalysisMode;
 import org.sonar.batch.analysis.AnalysisProperties;
 import com.google.common.collect.Maps;
 import org.junit.Rule;
@@ -38,6 +39,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
+import static org.mockito.Mockito.when;
+
+import static org.mockito.Mockito.mock;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class ProjectReactorBuilderTest {
@@ -45,6 +49,13 @@ public class ProjectReactorBuilderTest {
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
+  private AnalysisMode mode;
+  
+  @Before
+  public void setUp() {
+    mode = mock(AnalysisMode.class);
+  }
+  
   @Test
   public void shouldDefineSimpleProject() {
     ProjectDefinition projectDefinition = loadProjectDefinition("simple-project");
@@ -78,12 +89,11 @@ public class ProjectReactorBuilderTest {
   public void shouldNotFailIfBlankSourceDirectory() {
     loadProjectDefinition("simple-project-with-blank-source-dir");
   }
-  
+
   @Test
   public void modulesRepeatedIds() {
     thrown.expect(IllegalStateException.class);
     thrown.expectMessage("Two modules have the same id: module1");
-    
     loadProjectDefinition("multi-module-repeated-id");
   }
 
@@ -101,8 +111,8 @@ public class ProjectReactorBuilderTest {
     assertThat(rootProject.getTestDirs().contains("tests")).isFalse();
     assertThat(rootProject.getBinaries().contains("target/classes")).isFalse();
     // and module properties must have been cleaned
-    assertThat(rootProject.getProperties().getProperty("module1.sonar.projectKey")).isNull();
-    assertThat(rootProject.getProperties().getProperty("module2.sonar.projectKey")).isNull();
+    assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
+    assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
     // Check baseDir and workDir
     assertThat(rootProject.getBaseDir().getCanonicalFile())
       .isEqualTo(TestUtils.getResource(this.getClass(), "multi-module-definitions-all-in-root"));
@@ -125,8 +135,8 @@ public class ProjectReactorBuilderTest {
     assertThat(module1.getTestDirs()).contains("tests");
     assertThat(module1.getBinaries()).contains("target/classes");
     // and module properties must have been cleaned
-    assertThat(module1.getProperties().getProperty("module1.sonar.projectKey")).isNull();
-    assertThat(module1.getProperties().getProperty("module2.sonar.projectKey")).isNull();
+    assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
+    assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
     // Check baseDir and workDir
     assertThat(module1.getBaseDir().getCanonicalFile())
       .isEqualTo(TestUtils.getResource(this.getClass(), "multi-module-definitions-all-in-root/module1"));
@@ -144,8 +154,8 @@ public class ProjectReactorBuilderTest {
     assertThat(module2.getTestDirs()).contains("tests");
     assertThat(module2.getBinaries()).contains("target/classes");
     // and module properties must have been cleaned
-    assertThat(module2.getProperties().getProperty("module1.sonar.projectKey")).isNull();
-    assertThat(module2.getProperties().getProperty("module2.sonar.projectKey")).isNull();
+    assertThat(module2.properties().get("module1.sonar.projectKey")).isNull();
+    assertThat(module2.properties().get("module2.sonar.projectKey")).isNull();
     // Check baseDir and workDir
     assertThat(module2.getBaseDir().getCanonicalFile())
       .isEqualTo(TestUtils.getResource(this.getClass(), "multi-module-definitions-all-in-root/module2"));
@@ -160,8 +170,8 @@ public class ProjectReactorBuilderTest {
 
     // CHECK ROOT
     // module properties must have been cleaned
-    assertThat(rootProject.getProperties().getProperty("module1.sonar.moduleKey")).isNull();
-    assertThat(rootProject.getProperties().getProperty("module2.sonar.moduleKey")).isNull();
+    assertThat(rootProject.properties().get("module1.sonar.moduleKey")).isNull();
+    assertThat(rootProject.properties().get("module2.sonar.moduleKey")).isNull();
 
     // CHECK MODULES
     List<ProjectDefinition> modules = rootProject.getSubProjects();
@@ -301,8 +311,8 @@ public class ProjectReactorBuilderTest {
   public void multiModuleProperties() {
     ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root");
 
-    assertThat(projectDefinition.getProperties().getProperty("module11.property")).isNull();
-    assertThat(projectDefinition.getProperties().getProperty("sonar.profile")).isEqualTo("Foo");
+    assertThat(projectDefinition.properties().get("module11.property")).isNull();
+    assertThat(projectDefinition.properties().get("sonar.profile")).isEqualTo("Foo");
     ProjectDefinition module1 = null;
     ProjectDefinition module2 = null;
     for (ProjectDefinition prj : projectDefinition.getSubProjects()) {
@@ -312,12 +322,12 @@ public class ProjectReactorBuilderTest {
         module2 = prj;
       }
     }
-    assertThat(module1.getProperties().getProperty("module11.property")).isNull();
-    assertThat(module1.getProperties().getProperty("property")).isNull();
-    assertThat(module1.getProperties().getProperty("sonar.profile")).isEqualTo("Foo");
-    assertThat(module2.getProperties().getProperty("module11.property")).isNull();
-    assertThat(module2.getProperties().getProperty("property")).isNull();
-    assertThat(module2.getProperties().getProperty("sonar.profile")).isEqualTo("Foo");
+    assertThat(module1.properties().get("module11.property")).isNull();
+    assertThat(module1.properties().get("property")).isNull();
+    assertThat(module1.properties().get("sonar.profile")).isEqualTo("Foo");
+    assertThat(module2.properties().get("module11.property")).isNull();
+    assertThat(module2.properties().get("property")).isNull();
+    assertThat(module2.properties().get("sonar.profile")).isEqualTo("Foo");
 
     ProjectDefinition module11 = null;
     ProjectDefinition module12 = null;
@@ -328,13 +338,13 @@ public class ProjectReactorBuilderTest {
         module12 = prj;
       }
     }
-    assertThat(module11.getProperties().getProperty("module1.module11.property")).isNull();
-    assertThat(module11.getProperties().getProperty("module11.property")).isNull();
-    assertThat(module11.getProperties().getProperty("property")).isEqualTo("My module11 property");
-    assertThat(module11.getProperties().getProperty("sonar.profile")).isEqualTo("Foo");
-    assertThat(module12.getProperties().getProperty("module11.property")).isNull();
-    assertThat(module12.getProperties().getProperty("property")).isNull();
-    assertThat(module12.getProperties().getProperty("sonar.profile")).isEqualTo("Foo");
+    assertThat(module11.properties().get("module1.module11.property")).isNull();
+    assertThat(module11.properties().get("module11.property")).isNull();
+    assertThat(module11.properties().get("property")).isEqualTo("My module11 property");
+    assertThat(module11.properties().get("sonar.profile")).isEqualTo("Foo");
+    assertThat(module12.properties().get("module11.property")).isNull();
+    assertThat(module12.properties().get("property")).isNull();
+    assertThat(module12.properties().get("sonar.profile")).isEqualTo("Foo");
   }
 
   @Test
@@ -344,7 +354,7 @@ public class ProjectReactorBuilderTest {
     AnalysisProperties taskProperties = new AnalysisProperties(props, null);
     assertThat(taskProperties.property("module1.module11.property")).isEqualTo("My module11 property");
 
-    new ProjectReactorBuilder(taskProperties).execute();
+    new ProjectReactorBuilder(taskProperties, mode).execute();
 
     assertThat(taskProperties.property("module1.module11.property")).isNull();
   }
@@ -443,19 +453,27 @@ public class ProjectReactorBuilderTest {
 
   @Test
   public void shouldInitRootWorkDir() {
-    ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.<String, String>newHashMap(), null));
+    ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(Maps.<String, String>newHashMap(), null), mode);
     File baseDir = new File("target/tmp/baseDir");
 
     File workDir = builder.initRootProjectWorkDir(baseDir, Maps.<String, String>newHashMap());
 
     assertThat(workDir).isEqualTo(new File(baseDir, ".sonar"));
   }
+  
+  @Test
+  public void nonAssociatedMode() {
+    when(mode.isIssues()).thenReturn(true);
+    ProjectDefinition project = loadProjectDefinition("multi-module-with-basedir-not-associated");
+    
+    assertThat(project.getKey()).isEqualTo("project");
+  }
 
   @Test
   public void shouldInitWorkDirWithCustomRelativeFolder() {
     Map<String, String> props = Maps.<String, String>newHashMap();
     props.put("sonar.working.directory", ".foo");
-    ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null));
+    ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode);
     File baseDir = new File("target/tmp/baseDir");
 
     File workDir = builder.initRootProjectWorkDir(baseDir, props);
@@ -467,7 +485,7 @@ public class ProjectReactorBuilderTest {
   public void shouldInitRootWorkDirWithCustomAbsoluteFolder() {
     Map<String, String> props = Maps.<String, String>newHashMap();
     props.put("sonar.working.directory", new File("src").getAbsolutePath());
-    ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null));
+    ProjectReactorBuilder builder = new ProjectReactorBuilder(new AnalysisProperties(props, null), mode);
     File baseDir = new File("target/tmp/baseDir");
 
     File workDir = builder.initRootProjectWorkDir(baseDir, props);
@@ -477,16 +495,16 @@ public class ProjectReactorBuilderTest {
 
   @Test
   public void shouldFailIf2ModulesWithSameKey() {
-    Properties props = new Properties();
+    Map<String, String> props = new HashMap<>();
     props.put("sonar.projectKey", "root");
     ProjectDefinition root = ProjectDefinition.create().setProperties(props);
 
-    Properties props1 = new Properties();
+    Map<String, String> props1 = new HashMap<>();
     props1.put("sonar.projectKey", "mod1");
     root.addSubProject(ProjectDefinition.create().setProperties(props1));
 
     // Check uniqueness of a new module: OK
-    Properties props2 = new Properties();
+    Map<String, String> props2 = new HashMap<>();
     props2.put("sonar.projectKey", "mod2");
     ProjectDefinition mod2 = ProjectDefinition.create().setProperties(props2);
     ProjectReactorBuilder.checkUniquenessOfChildKey(mod2, root);
@@ -519,7 +537,7 @@ public class ProjectReactorBuilderTest {
   private ProjectDefinition loadProjectDefinition(String projectFolder) {
     Map<String, String> props = loadProps(projectFolder);
     AnalysisProperties bootstrapProps = new AnalysisProperties(props, null);
-    ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps).execute();
+    ProjectReactor projectReactor = new ProjectReactorBuilder(bootstrapProps,mode).execute();
     return projectReactor.getRoot();
   }
   
@@ -594,8 +612,8 @@ public class ProjectReactorBuilderTest {
     assertThat(rootProject.getTestDirs().contains("tests")).isFalse();
     assertThat(rootProject.getBinaries().contains("target/classes")).isFalse();
     // and module properties must have been cleaned
-    assertThat(rootProject.getProperties().getProperty("module1.sonar.projectKey")).isNull();
-    assertThat(rootProject.getProperties().getProperty("module2.sonar.projectKey")).isNull();
+    assertThat(rootProject.properties().get("module1.sonar.projectKey")).isNull();
+    assertThat(rootProject.properties().get("module2.sonar.projectKey")).isNull();
     // Check baseDir and workDir
     assertThat(rootProject.getBaseDir().getCanonicalFile())
       .isEqualTo(TestUtils.getResource(this.getClass(), "multi-module-definitions-same-prefix"));
@@ -618,8 +636,8 @@ public class ProjectReactorBuilderTest {
     assertThat(module1.getTestDirs()).contains("tests");
     assertThat(module1.getBinaries()).contains("target/classes");
     // and module properties must have been cleaned
-    assertThat(module1.getProperties().getProperty("module1.sonar.projectKey")).isNull();
-    assertThat(module1.getProperties().getProperty("module2.sonar.projectKey")).isNull();
+    assertThat(module1.properties().get("module1.sonar.projectKey")).isNull();
+    assertThat(module1.properties().get("module2.sonar.projectKey")).isNull();
     // Check baseDir and workDir
     assertThat(module1.getBaseDir().getCanonicalFile())
       .isEqualTo(TestUtils.getResource(this.getClass(), "multi-module-definitions-same-prefix/module1"));
@@ -637,8 +655,8 @@ public class ProjectReactorBuilderTest {
     assertThat(module1Feature.getTestDirs()).contains("tests");
     assertThat(module1Feature.getBinaries()).contains("target/classes");
     // and module properties must have been cleaned
-    assertThat(module1Feature.getProperties().getProperty("module1.sonar.projectKey")).isNull();
-    assertThat(module1Feature.getProperties().getProperty("module2.sonar.projectKey")).isNull();
+    assertThat(module1Feature.properties().get("module1.sonar.projectKey")).isNull();
+    assertThat(module1Feature.properties().get("module2.sonar.projectKey")).isNull();
     // Check baseDir and workDir
     assertThat(module1Feature.getBaseDir().getCanonicalFile())
       .isEqualTo(TestUtils.getResource(this.getClass(), "multi-module-definitions-same-prefix/module1.feature"));
index acc6fa82c34fd6d69d6f415c7d8434b91fb47a4c..1cb70ff6dcb6596ed5bc6e210902f9fe3300216b 100644 (file)
@@ -27,7 +27,6 @@ import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.api.config.Settings;
-import org.sonar.api.utils.SonarException;
 
 public class ProjectReactorValidatorTest {
 
@@ -90,7 +89,7 @@ public class ProjectReactorValidatorTest {
   public void fail_with_invalid_key() {
     ProjectReactor reactor = createProjectReactor("foo$bar");
 
-    thrown.expect(SonarException.class);
+    thrown.expect(IllegalStateException.class);
     thrown.expectMessage("\"foo$bar\" is not a valid project or module key");
     validator.validate(reactor);
   }
@@ -99,7 +98,7 @@ public class ProjectReactorValidatorTest {
   public void fail_with_backslash_in_key() {
     ProjectReactor reactor = createProjectReactor("foo\\bar");
 
-    thrown.expect(SonarException.class);
+    thrown.expect(IllegalStateException.class);
     thrown.expectMessage("\"foo\\bar\" is not a valid project or module key");
     validator.validate(reactor);
   }
@@ -117,7 +116,7 @@ public class ProjectReactorValidatorTest {
   @Test
   public void fail_with_invalid_branch() {
     ProjectReactor reactor = createProjectReactor("foo", "bran#ch");
-    thrown.expect(SonarException.class);
+    thrown.expect(IllegalStateException.class);
     thrown.expectMessage("\"bran#ch\" is not a valid branch name");
     validator.validate(reactor);
   }
@@ -125,7 +124,7 @@ public class ProjectReactorValidatorTest {
   @Test
   public void fail_with_colon_in_branch() {
     ProjectReactor reactor = createProjectReactor("foo", "bran:ch");
-    thrown.expect(SonarException.class);
+    thrown.expect(IllegalStateException.class);
     thrown.expectMessage("\"bran:ch\" is not a valid branch name");
     validator.validate(reactor);
   }
@@ -134,7 +133,7 @@ public class ProjectReactorValidatorTest {
   public void fail_with_only_digits() {
     ProjectReactor reactor = createProjectReactor("12345");
 
-    thrown.expect(SonarException.class);
+    thrown.expect(IllegalStateException.class);
     thrown.expectMessage("\"12345\" is not a valid project or module key");
     validator.validate(reactor);
   }
@@ -144,7 +143,7 @@ public class ProjectReactorValidatorTest {
     ProjectReactor reactor = createProjectReactor("foo");
     settings.setProperty("sonar.phase", "phase");
 
-    thrown.expect(SonarException.class);
+    thrown.expect(IllegalStateException.class);
     thrown.expectMessage("\"sonar.phase\" is deprecated");
     validator.validate(reactor);
   }
index de99ed8e909cbd660d4616a6580db77d6e1ff45a..412b563e2ac85418c379fde2377b5df9c5a4be4c 100644 (file)
  */
 package org.sonar.batch.scan;
 
-import org.sonar.batch.analysis.DefaultAnalysisMode;
+import org.sonar.batch.protocol.input.FileData;
 
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import com.google.common.collect.ImmutableTable;
+import org.sonar.batch.repository.ProjectSettingsRepo;
+import org.sonar.batch.analysis.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.GlobalMode;
 import com.google.common.collect.ImmutableMap;
 
@@ -40,7 +45,6 @@ import org.sonar.api.utils.log.LoggerLevel;
 import org.sonar.batch.bootstrap.GlobalProperties;
 import org.sonar.batch.bootstrap.GlobalSettings;
 import org.sonar.batch.protocol.input.GlobalRepositories;
-import org.sonar.batch.protocol.input.ProjectRepositories;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -52,16 +56,20 @@ public class ProjectSettingsTest {
   @Rule
   public LogTester logTester = new LogTester();
 
-  ProjectRepositories projectRef;
-  ProjectDefinition project = ProjectDefinition.create().setKey("struts");
-  GlobalSettings bootstrapProps;
+  private ProjectSettingsRepo projectRef;
+  private ProjectDefinition project;
+  private GlobalSettings bootstrapProps;
+  private Table<String, String, FileData> emptyFileData;
+  private Table<String, String, String> emptySettings;
 
   private GlobalMode globalMode;
   private DefaultAnalysisMode mode;
 
   @Before
   public void prepare() {
-    projectRef = new ProjectRepositories();
+    emptyFileData = ImmutableTable.of();
+    emptySettings = ImmutableTable.of();
+    project = ProjectDefinition.create().setKey("struts");
     globalMode = mock(GlobalMode.class);
     mode = mock(DefaultAnalysisMode.class);
     bootstrapProps = new GlobalSettings(new GlobalProperties(Collections.<String, String>emptyMap()), new PropertyDefinitions(), new GlobalRepositories(), globalMode);
@@ -71,6 +79,7 @@ public class ProjectSettingsTest {
   public void should_load_project_props() {
     project.setProperty("project.prop", "project");
 
+    projectRef = new ProjectSettingsRepo(emptySettings, emptyFileData, null);
     ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
 
     assertThat(batchSettings.getString("project.prop")).isEqualTo("project");
@@ -78,10 +87,12 @@ public class ProjectSettingsTest {
 
   @Test
   public void should_load_project_root_settings() {
-    projectRef.addSettings("struts", ImmutableMap.of("sonar.cpd.cross", "true", "sonar.java.coveragePlugin", "jacoco"));
+    Table<String, String, String> settings = HashBasedTable.create();
+    settings.put("struts", "sonar.cpd.cross", "true");
+    settings.put("struts", "sonar.java.coveragePlugin", "jacoco");
 
+    projectRef = new ProjectSettingsRepo(settings, emptyFileData, null);
     ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
-
     assertThat(batchSettings.getString("sonar.java.coveragePlugin")).isEqualTo("jacoco");
   }
 
@@ -89,7 +100,11 @@ public class ProjectSettingsTest {
   public void should_load_project_root_settings_on_branch() {
     project.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "mybranch");
 
-    projectRef.addSettings("struts:mybranch", ImmutableMap.of("sonar.cpd.cross", "true", "sonar.java.coveragePlugin", "jacoco"));
+    Table<String, String, String> settings = HashBasedTable.create();
+    settings.put("struts:mybranch", "sonar.cpd.cross", "true");
+    settings.put("struts:mybranch", "sonar.java.coveragePlugin", "jacoco");
+
+    projectRef = new ProjectSettingsRepo(settings, emptyFileData, null);
 
     ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
 
@@ -98,8 +113,11 @@ public class ProjectSettingsTest {
 
   @Test
   public void should_not_fail_when_accessing_secured_properties() {
-    projectRef.addSettings("struts", ImmutableMap.of("sonar.foo.secured", "bar", "sonar.foo.license.secured", "bar2"));
+    Table<String, String, String> settings = HashBasedTable.create();
+    settings.put("struts", "sonar.foo.secured", "bar");
+    settings.put("struts", "sonar.foo.license.secured", "bar2");
 
+    projectRef = new ProjectSettingsRepo(settings, emptyFileData, null);
     ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
 
     assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
@@ -108,10 +126,13 @@ public class ProjectSettingsTest {
 
   @Test
   public void should_fail_when_accessing_secured_properties_in_issues_mode() {
-    projectRef.addSettings("struts", ImmutableMap.of("sonar.foo.secured", "bar", "sonar.foo.license.secured", "bar2"));
+    Table<String, String, String> settings = HashBasedTable.create();
+    settings.put("struts", "sonar.foo.secured", "bar");
+    settings.put("struts", "sonar.foo.license.secured", "bar2");
 
     when(mode.isIssues()).thenReturn(true);
 
+    projectRef = new ProjectSettingsRepo(settings, emptyFileData, null);
     ProjectSettings batchSettings = new ProjectSettings(new ProjectReactor(project), bootstrapProps, new PropertyDefinitions(), projectRef, mode);
 
     assertThat(batchSettings.getString("sonar.foo.license.secured")).isEqualTo("bar2");
@@ -123,10 +144,13 @@ public class ProjectSettingsTest {
 
   @Test
   public void should_log_a_warning_when_a_dropper_property_is_present() {
-    GlobalSettings settings = new GlobalSettings(new GlobalProperties(ImmutableMap.of("sonar.qualitygate", "somevalue")), new PropertyDefinitions(), new GlobalRepositories(), globalMode);
+    GlobalSettings settings = new GlobalSettings(new GlobalProperties(ImmutableMap.of("sonar.qualitygate", "somevalue")), new PropertyDefinitions(), new GlobalRepositories(),
+      globalMode);
+    projectRef = new ProjectSettingsRepo(emptySettings, emptyFileData, null);
     new ProjectSettings(new ProjectReactor(project), settings, new PropertyDefinitions(), projectRef, mode);
 
     assertThat(logTester.logs(LoggerLevel.WARN)).containsOnly("Property 'sonar.qualitygate' is not supported any more. It will be ignored.");
 
   }
+
 }
index 53f5db286512f65ae821b34f03b69fa9a2dd2d98..1e2f3ac36200d42d973a4d5c8180af1aa75139d9 100644 (file)
  */
 package org.sonar.batch.scan.filesystem;
 
-import org.junit.Test;
-import org.sonar.batch.protocol.input.ProjectRepositories;
+import org.sonar.batch.repository.ProjectSettingsRepo;
 
+import org.junit.Test;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 public class StatusDetectionFactoryTest {
   @Test
   public void testCreate() throws Exception {
-    StatusDetectionFactory factory = new StatusDetectionFactory(mock(ProjectRepositories.class));
+    StatusDetectionFactory factory = new StatusDetectionFactory(mock(ProjectSettingsRepo.class));
     StatusDetection detection = factory.create();
     assertThat(detection).isNotNull();
   }
index c8db34b45428022c626b7dc00544e979d4ef287a..7aa79e5554e74858ec8ef28fea702618132946ff 100644 (file)
  */
 package org.sonar.batch.scan.filesystem;
 
+import com.google.common.collect.ImmutableTable;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+
+import org.sonar.batch.repository.ProjectSettingsRepo;
 import org.junit.Test;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.batch.protocol.input.FileData;
-import org.sonar.batch.protocol.input.ProjectRepositories;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class StatusDetectionTest {
   @Test
   public void detect_status() {
-    ProjectRepositories ref = new ProjectRepositories();
-    ref.addFileData("foo", "src/Foo.java", new FileData("ABCDE", true));
-    ref.addFileData("foo", "src/Bar.java", new FileData("FGHIJ", true));
+    Table<String, String, String> t = ImmutableTable.of();
+    ProjectSettingsRepo ref = new ProjectSettingsRepo(t, createTable(), null);
     StatusDetection statusDetection = new StatusDetection(ref);
 
     assertThat(statusDetection.status("foo", "src/Foo.java", "ABCDE")).isEqualTo(InputFile.Status.SAME);
     assertThat(statusDetection.status("foo", "src/Foo.java", "XXXXX")).isEqualTo(InputFile.Status.CHANGED);
     assertThat(statusDetection.status("foo", "src/Other.java", "QWERT")).isEqualTo(InputFile.Status.ADDED);
   }
+
+  private static Table<String, String, FileData> createTable() {
+    Table<String, String, FileData> t = HashBasedTable.create();
+
+    t.put("foo", "src/Foo.java", new FileData("ABCDE", true));
+    t.put("foo", "src/Bar.java", new FileData("FGHIJ", true));
+
+    return t;
+  }
 }
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a1/src/main/xoo/com/sonar/it/samples/modules/a1/HelloA1.xoo
new file mode 100644 (file)
index 0000000..74d29a4
--- /dev/null
@@ -0,0 +1,16 @@
+package com.sonar.it.samples.modules.a1;
+
+public class HelloA1 {
+       private int i;
+       private HelloA1() {
+               
+       }
+       
+       public void hello() {
+               System.out.println("hello" + " xoo");
+       }
+       
+       protected String getHello() {
+               return "hello";
+       }
+}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_a/module_a2/src/main/xoo/com/sonar/it/samples/modules/a2/HelloA2.xoo
new file mode 100644 (file)
index 0000000..4203953
--- /dev/null
@@ -0,0 +1,12 @@
+package com.sonar.it.samples.modules.a2;
+
+public class HelloA2 {
+       private int i;
+       private HelloA2() {
+               
+       }
+       
+       public void hello() {
+               System.out.println("hello" + " xoo");
+       }
+}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b1/src/main/xoo/com/sonar/it/samples/modules/b1/HelloB1.xoo
new file mode 100644 (file)
index 0000000..b83c3af
--- /dev/null
@@ -0,0 +1,12 @@
+package com.sonar.it.samples.modules.b1;
+
+public class HelloB1 {
+       private int i;
+       private HelloB1() {
+               
+       }
+       
+       public void hello() {
+               System.out.println("hello" + " world");
+       }
+}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/module_b/module_b2/src/main/xoo/com/sonar/it/samples/modules/b2/HelloB2.xoo
new file mode 100644 (file)
index 0000000..20b8bb3
--- /dev/null
@@ -0,0 +1,12 @@
+package com.sonar.it.samples.modules.b2;
+
+public class HelloB2 {
+       private int i;
+       private HelloB2() {
+               
+       }
+       
+       public void hello() {
+               System.out.println("hello" + " world");
+       }
+}
\ No newline at end of file
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties b/sonar-batch/src/test/resources/mediumtest/xoo/multi-modules-sample-not-associated/sonar-project.properties
new file mode 100644 (file)
index 0000000..c2b00ed
--- /dev/null
@@ -0,0 +1,31 @@
+# Root project information
+#sonar.projectKey=com.sonarsource.it.samples:multi-modules-sample
+sonar.projectName=Sonar :: Integration Tests :: Multi-modules Sample
+sonar.projectVersion=1.0-SNAPSHOT
+
+sonar.language=xoo
+# Some properties that will be inherited by the modules
+sonar.sources=src/main/xoo
+# List of the module identifiers
+sonar.modules=module_a,module_b
+
+module_a.sonar.projectKey=module_a
+module_a.sonar.projectName=Module A
+module_a.sonar.modules=module_a1,module_a2
+
+module_a.module_a1.sonar.projectName=Sub-module A1
+
+module_a.module_a2.sonar.projectName=Sub-module A2
+
+
+module_b.sonar.projectKey=module_b
+module_b.sonar.projectName=Module B
+module_b.sonar.modules=module_b1,module_b2
+
+module_b.module_b1.sonar.projectName=Sub-module B1
+
+module_b.module_b2.sonar.projectName=Sub-module B2
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text b/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_GitScmProvider.text
deleted file mode 100644 (file)
index a9ad538..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-523048e7f5ca9550505f2d8ea6d587e7
-50ff1975ec4309da19591231c6b5104b
-eba1d423f8632818ce94c4eac1b90713
-6112a40c70ed55453a0753030d5564a4
-3389dae361af79b04c9c8e7057f60cc6
-eac5fc1130394e7268b1cfbc54cd7e4d
-c0b153d8c08365f2de343e278d3b54c7
-eb4521cb5d193e1d37ecac25b0ffea43
-9210ed0dec59ed663c744d7fb68f0275
-3389dae361af79b04c9c8e7057f60cc6
-cd0fbdfa49d32525ecbdb8dab19dafe6
-ea12a10f5b7730daa639fe133867e088
-69739b9bc9312dfb1a6b8625a08c652a
-ec21e054f7f5748d0161fe27cdad6462
-3389dae361af79b04c9c8e7057f60cc6
-951a83e8074813100da0cba92092b385
-c93caecd79a332773cfb06cd5d3b8895
-5832d52d5fcb22a3350f62c856993f0d
-c4c9bdd47ee05028cb84873da0ebf2b5
-f89e422b117e518acef69df33f199d10
-
-90aa2aae2384f6412c3b86d085d5ffa5
-647f262205ad09f32b0091df388992ed
-
-943d54ba3e8812437c4d26ef8aa263f8
-
-340385b760d1441d3b74e5e39399cc0c
-
-a94613fd32125cd63160b0c1cf2bd078
-
-3415664f5f4a608772e6a4c73a993804
-597b7f5598c56e77bd28b9ff15a30802
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-2c953c12d2eb6ea958b7f3045ecf8e81
-864d4d5a0cd65f52d791700443cec75e
-1b2437750694bba602fedc0a568c65de
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-2c953c12d2eb6ea958b7f3045ecf8e81
-d18a921e891f6f9af8564a882efea289
-8bc5ef2851a7dcf1cf096a68e2a47ba6
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-2c953c12d2eb6ea958b7f3045ecf8e81
-9cf0f8aa69740d88788fb437589ed33f
-0df2777822bbc7799716a10478ca58d4
-cbb184dd8e05c9709e5dcaedaa0495cf
-cbb184dd8e05c9709e5dcaedaa0495cf
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text b/sonar-batch/src/test/resources/org/sonar/batch/cache/ProjectCacheSynchronizerTest/api_sources_hash_JGitBlameCommand.text
deleted file mode 100644 (file)
index e21a25b..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-523048e7f5ca9550505f2d8ea6d587e7
-50ff1975ec4309da19591231c6b5104b
-eba1d423f8632818ce94c4eac1b90713
-6112a40c70ed55453a0753030d5564a4
-3389dae361af79b04c9c8e7057f60cc6
-eac5fc1130394e7268b1cfbc54cd7e4d
-c0b153d8c08365f2de343e278d3b54c7
-eb4521cb5d193e1d37ecac25b0ffea43
-9210ed0dec59ed663c744d7fb68f0275
-3389dae361af79b04c9c8e7057f60cc6
-cd0fbdfa49d32525ecbdb8dab19dafe6
-ea12a10f5b7730daa639fe133867e088
-69739b9bc9312dfb1a6b8625a08c652a
-ec21e054f7f5748d0161fe27cdad6462
-3389dae361af79b04c9c8e7057f60cc6
-951a83e8074813100da0cba92092b385
-c93caecd79a332773cfb06cd5d3b8895
-5832d52d5fcb22a3350f62c856993f0d
-c4c9bdd47ee05028cb84873da0ebf2b5
-f89e422b117e518acef69df33f199d10
-
-9e0ae10d6ada18721c856844d765b465
-ea3c894506f93b88c9fc6c9790da9008
-c5c303a0f47f5f15f22b6776fc1c8c93
-4f592acdcfc11c97e7f19231de9d69b0
-6aea6951956275cb62d01063a1e695fe
-293f7a3f08e54359c17d5e984f721665
-18d24bd6a2c2c15d3914502e2776e372
-107e08f15be7e18888da7e69948ac3ba
-90aa2aae2384f6412c3b86d085d5ffa5
-ef76944333105582ae8d3a51d29b3b8a
-2a592c3d07126847ae4cbbed4a2b4d46
-6f382821d6f35beb6ae4080607046898
-
-943d54ba3e8812437c4d26ef8aa263f8
-6fa05171389dfbeda44181c98e580d18
-391715e38dad3a13b75205d527e82c8a
-bb3900a63a8cecc1e79592e054915c97
-7c8d40302b1200413bc859331a4f241d
-0e2ad2ad1ad56b970e4348a3967d1e81
-049ace1ff1516be8a5fd7cddc0ab2f30
-d2c44db3922004ac2ae41fb402d005b1
-b0ba7766e9e1fddaf40e86b62a5c2900
-
-62e220c0092e8ae61f5937f18e4b03bc
-
-a5eeb3bd06bd4499f8a9ad20ba426bbd
-
-2bb44a6b46970b3efd87cc8a68848fae
-
-5b9e24bad64529f3e35ba4f1aed892f2
-78f4e1ffbbf29629025b20f6b1be36f5
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-2c953c12d2eb6ea958b7f3045ecf8e81
-0f253056876c021c4fd3f3e5ddc4d5b6
-520e98566046a173f9250aa3b7a40ec9
-c31394024dee65cba7e5c526c69af278
-80d5b17efc16ace990c07580fc3e85eb
-d28bc6d296024d650b16efe1369128b0
-38ba857d93d3a54a6b7f1bfc1b8fb090
-d41f14cd3267e8d9c17b47ddcb71b0c1
-54f339c05c18199eca937a31fdc07857
-7efc34bf8e2ba01cec26c50875ac8acd
-5a29aee8cfe3a5110dc892ab8adfc17f
-d271e10accbea4cb6365b85150505b0d
-cbb184dd8e05c9709e5dcaedaa0495cf
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-319f70c2d340b01002c3539d09dabbec
-b225a5ae163bfc56684d172522993825
-791c95e71dd996b4d723f96df3f37ca4
-80d5b17efc16ace990c07580fc3e85eb
-2a8af480cbdcc0ee9b44187a078c8fe7
-4340b548cda0dbc6043cbf4cf49d1b12
-2df758e8d85494e7ee23a02d4c3aa6a7
-e44feb14b61ab99767239eaded464459
-eb375774c265dedeefeb29283ceea9bc
-c87c662bc284c5f9c01d3551957ee32e
-cbb184dd8e05c9709e5dcaedaa0495cf
-cbb184dd8e05c9709e5dcaedaa0495cf
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-1fcefdfae441ded6478da69428a30f12
-8fcd1ffa896d0e214539b1bfa179f3e8
-7a64b5f6e57cc9f13d5a1be99b9f7c23
-8884d72adb8d93474c6af620a7d6fdba
-cbb184dd8e05c9709e5dcaedaa0495cf
-7e53b1d2085d8c7ae88417eccc5a0893
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-5f68d8059a28922be107658c3890c230
-6ded45bc62f1d535345e67001dac69d5
-34165242e2230ffca31d7942ec577e6b
-02f5ec563dbaaef9ef402c6941a36c98
-784b65e21ca0539f3cefb1710ccc7768
-982e86cf85e0f121ba1a2f0ef462aa6b
-cbb184dd8e05c9709e5dcaedaa0495cf
-80d5b17efc16ace990c07580fc3e85eb
-34c0d764eb79cc7c6dccb53e711fd4be
-a5b2391dd7127292b7240c7c8c1ee92a
-54d7949984c901073fffda9956190c12
-da38c234aa3fdca9ec1e0e2b991d3568
-6b2decb38be3882440910fd75ec508cd
-ce51581950deb12616108c0e909f9c53
-cbb184dd8e05c9709e5dcaedaa0495cf
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-e8165f1ae4cf11035542d4b60ac7b14d
-c8272ad357e7feaf2671a0612e52d3b5
-2c953c12d2eb6ea958b7f3045ecf8e81
-209935c0f7c635d91164fe2b14314a3d
-448b1c0a64288dbeac516ba67c9f2574
-540c13e9e156b687226421b24f2df178
-cbb184dd8e05c9709e5dcaedaa0495cf
-c0c97e22db5055def551d1cef15fb251
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-8983f749749a87599ea48c8bc08cac86
-f298d78afda5708f64ded32af0e7f541
-248e216059cad9c12bc8ff8b3b6289e2
-80d5b17efc16ace990c07580fc3e85eb
-318ddd60a422c3d0435acd5f7aa6923c
-7c3bca9656325d15227ffac4ce5dfde9
-7d992ab2241888573c1c7bdfe5d33a35
-cd620097a91b074942d29535e822ebb0
-a50e423de97896afb97123424a960664
-35bb4ed816814a612f9605aec97c69fb
-cbb184dd8e05c9709e5dcaedaa0495cf
-280db1f26dcc577de8fc62d40627112b
-e5b027822061ae041ced3d958b6f7f37
-11a0dde589dec02e8d95c4ea6d83780f
-e5acab4c66ae60432ccc5a45d718d152
-3c4b4570f7e4c7037693b1aa1fd5a9ca
-420d9af4c91e3248bb2a4e72a683a03a
-d69bf14adaf9ea45b48c0fdfefa4f69d
-505b97969baa28c3f607a38ee02f4f2d
-cbb184dd8e05c9709e5dcaedaa0495cf
-da94121a62e940229bd622927ad7702f
-9d2a20a55b185c4a864efd3484a870fa
-cbb184dd8e05c9709e5dcaedaa0495cf
-daada2d57491a2d0b4d237f29fc039dc
-53220ad5b69915dec696a01167d5237b
-2fb05fbd558fb020eacca16faa325246
-cbb184dd8e05c9709e5dcaedaa0495cf
-423c485e2882c1fd9a1b19983b812f50
-cbb184dd8e05c9709e5dcaedaa0495cf
-
-cbb184dd8e05c9709e5dcaedaa0495cf
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java b/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/modules/module1/sources/Fake.java
new file mode 100644 (file)
index 0000000..e67004d
--- /dev/null
@@ -0,0 +1 @@
+class Fake {}
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties b/sonar-batch/src/test/resources/org/sonar/batch/scan/ProjectReactorBuilderTest/multi-module-with-basedir-not-associated/sonar-project.properties
new file mode 100644 (file)
index 0000000..c572ef1
--- /dev/null
@@ -0,0 +1,14 @@
+#sonar.projectKey=com.foo.project
+sonar.projectName=Foo Project
+sonar.projectVersion=1.0-SNAPSHOT
+sonar.projectDescription=Description of Foo Project
+
+sonar.sources=sources
+sonar.tests=tests
+sonar.binaries=target/classes
+
+sonar.modules=module1
+
+module1.sonar.projectBaseDir=modules/module1
+module1.sonar.projectKey=com.foo.project.module1
+module1.sonar.projectName=Foo Module 1
index 922a59e5e8d261df007fc0474f9f136c4359ef77..6d926a918e693ce146a6c069f971f2a33104d3a5 100644 (file)
@@ -117,7 +117,7 @@ public class FileCache {
     return new File(dir, hash);
   }
 
-  private void mkdirQuietly(File hashDir) {
+  private static void mkdirQuietly(File hashDir) {
     try {
       Files.createDirectories(hashDir.toPath());
     } catch (IOException e) {
index 549f8d10f63917795fb5cc5fe831c570607a7f70..e79a9b4fd7475ae842d6438211183f6dc8300087 100644 (file)
@@ -56,7 +56,7 @@ public class FileHashes {
     }
   }
 
-  private byte[] digest(InputStream input, MessageDigest digest) throws IOException {
+  private static byte[] digest(InputStream input, MessageDigest digest) throws IOException {
     final byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
     int read = input.read(buffer, 0, STREAM_BUFFER_LENGTH);
     while (read > -1) {
index c5c45090ed477cb2699ccb82dc008d5fda5c2526..2b32c65b7f970ca774667d44b308e32e5b5d79d0 100644 (file)
@@ -19,7 +19,9 @@
  */
 package org.sonar.home.cache;
 
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.RandomAccessFile;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
@@ -28,6 +30,7 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -77,7 +80,7 @@ public class PersistentCache {
   @CheckForNull
   public synchronized String getString(@Nonnull String obj, @Nullable final PersistentCacheLoader<String> valueLoader) throws IOException {
     ValueLoaderDecoder decoder = valueLoader != null ? new ValueLoaderDecoder(valueLoader) : null;
-    
+
     byte[] cached = get(obj, decoder);
 
     if (cached == null) {
@@ -87,6 +90,20 @@ public class PersistentCache {
     return new String(cached, ENCODING);
   }
 
+  @CheckForNull
+  public synchronized InputStream getStream(@Nonnull String obj) throws IOException {
+    String key = getKey(obj);
+
+    try {
+      lock();
+      Path path = getCacheCopy(key);
+      return new DeleteOnCloseInputStream(new FileInputStream(path.toFile()), path);
+
+    } finally {
+      unlock();
+    }
+  }
+
   @CheckForNull
   public synchronized byte[] get(@Nonnull String obj, @Nullable PersistentCacheLoader<byte[]> valueLoader) throws IOException {
     String key = getKey(obj);
@@ -116,6 +133,16 @@ public class PersistentCache {
 
     return null;
   }
+  
+  public synchronized void put(@Nonnull String obj, @Nonnull InputStream stream) throws IOException {
+    String key = getKey(obj);
+    try {
+      lock();
+      putCache(key, stream);
+    } finally {
+      unlock();
+    }
+  }
 
   public synchronized void put(@Nonnull String obj, @Nonnull byte[] value) throws IOException {
     String key = getKey(obj);
@@ -266,6 +293,11 @@ public class PersistentCache {
     Path cachePath = getCacheEntryPath(key);
     Files.write(cachePath, value, CREATE, WRITE, TRUNCATE_EXISTING);
   }
+  
+  private void putCache(String key, InputStream stream) throws IOException {
+    Path cachePath = getCacheEntryPath(key);
+    Files.copy(stream,  cachePath, StandardCopyOption.REPLACE_EXISTING);
+  }
 
   private byte[] getCache(String key) throws IOException {
     Path cachePath = getCacheEntryPath(key);
@@ -277,6 +309,39 @@ public class PersistentCache {
     return Files.readAllBytes(cachePath);
   }
 
+  private Path getCacheCopy(String key) throws IOException {
+    Path cachePath = getCacheEntryPath(key);
+
+    if (!validateCacheEntry(cachePath, this.defaultDurationToExpireMs)) {
+      return null;
+    }
+
+    Path temp = Files.createTempFile("sonar_cache", null);
+    Files.copy(cachePath, temp, StandardCopyOption.REPLACE_EXISTING);
+    return temp;
+  }
+
+  private static class DeleteOnCloseInputStream extends InputStream {
+    private final InputStream stream;
+    private final Path p;
+
+    private DeleteOnCloseInputStream(InputStream stream, Path p) {
+      this.stream = stream;
+      this.p = p;
+    }
+
+    @Override
+    public int read() throws IOException {
+      return stream.read();
+    }
+
+    @Override
+    public void close() throws IOException {
+      stream.close();
+      Files.delete(p);
+    }
+  }
+
   private boolean validateCacheEntry(Path cacheEntryPath, long durationToExpireMs) throws IOException {
     if (!Files.exists(cacheEntryPath)) {
       return false;