]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6169 Fix lifecycle issue
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 9 Feb 2015 15:05:47 +0000 (16:05 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Mon, 9 Feb 2015 15:11:27 +0000 (16:11 +0100)
Previous blame should be loaded after ProjectBuilders are executed

sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java
sonar-batch/src/main/java/org/sonar/batch/repository/ProjectScmRepositoryLoader.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java

index a4c455178f9a076a150081d81290a3708fa84dfc..772f6923466ba173093d7fd1d73e54d5d94b8fe0 100644 (file)
  */
 package org.sonar.batch.repository;
 
-import com.google.common.collect.Maps;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.database.DatabaseSession;
-import org.sonar.api.database.model.MeasureModel;
-import org.sonar.api.database.model.ResourceModel;
-import org.sonar.api.database.model.Snapshot;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Qualifiers;
 import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.bootstrap.TaskProperties;
-import org.sonar.batch.protocol.input.FileData;
 import org.sonar.batch.protocol.input.ProjectRepositories;
 import org.sonar.batch.rule.ModuleQProfiles;
 
-import javax.annotation.CheckForNull;
-import javax.persistence.NoResultException;
-import javax.persistence.Query;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import static org.sonar.api.utils.DateUtils.longToDate;
-
 public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoader {
 
   private static final Logger LOG = LoggerFactory.getLogger(DefaultProjectRepositoriesLoader.class);
@@ -57,16 +36,8 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
 
   private final ServerClient serverClient;
   private final DefaultAnalysisMode analysisMode;
-  private final DatabaseSession session;
-
-  public DefaultProjectRepositoriesLoader(DatabaseSession session, ServerClient serverClient, DefaultAnalysisMode analysisMode) {
-    this.session = session;
-    this.serverClient = serverClient;
-    this.analysisMode = analysisMode;
-  }
 
   public DefaultProjectRepositoriesLoader(ServerClient serverClient, DefaultAnalysisMode analysisMode) {
-    this.session = null;
     this.serverClient = serverClient;
     this.analysisMode = analysisMode;
   }
@@ -81,98 +52,7 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad
       url += "&profile=" + ServerClient.encodeForUrl(taskProperties.properties().get(ModuleQProfiles.SONAR_PROFILE_PROP));
     }
     url += "&preview=" + analysisMode.isPreview();
-    ProjectRepositories ref = ProjectRepositories.fromJson(serverClient.request(url));
-
-    if (session != null) {
-      for (ProjectDefinition module : reactor.getProjects()) {
-
-        for (Entry<String, FileData> fileDataByPaths : ref.fileDataByPath(module.getKeyWithBranch()).entrySet()) {
-          String path = fileDataByPaths.getKey();
-          FileData fileData = fileDataByPaths.getValue();
-          String lastCommits = null;
-          String revisions = null;
-          String authors = null;
-          List<Object[]> measuresByKey = query(projectKey + ":" + path, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY, CoreMetrics.SCM_REVISIONS_BY_LINE_KEY,
-            CoreMetrics.SCM_AUTHORS_BY_LINE_KEY);
-          for (Object[] measureByKey : measuresByKey) {
-            if (measureByKey[0].equals(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY)) {
-              lastCommits = ((MeasureModel) measureByKey[1]).getData(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
-            } else if (measureByKey[0].equals(CoreMetrics.SCM_REVISIONS_BY_LINE_KEY)) {
-              revisions = ((MeasureModel) measureByKey[1]).getData(CoreMetrics.SCM_REVISIONS_BY_LINE);
-            } else if (measureByKey[0].equals(CoreMetrics.SCM_AUTHORS_BY_LINE_KEY)) {
-              authors = ((MeasureModel) measureByKey[1]).getData(CoreMetrics.SCM_AUTHORS_BY_LINE);
-            }
-          }
-          ref.addFileData(module.getKeyWithBranch(), path, new FileData(fileData.hash(), authors == null, lastCommits, revisions, authors));
-        }
-      }
-      ref.setLastAnalysisDate(lastSnapshotCreationDate(projectKey));
-    }
-    return ref;
-  }
-
-  public List<Object[]> query(String resourceKey, String... metricKeys) {
-    StringBuilder sb = new StringBuilder();
-    Map<String, Object> params = Maps.newHashMap();
-
-    sb.append("SELECT met.key, m");
-    sb.append(" FROM ")
-      .append(MeasureModel.class.getSimpleName())
-      .append(" m, ")
-      .append(Metric.class.getSimpleName())
-      .append(" met, ")
-      .append(ResourceModel.class.getSimpleName())
-      .append(" r, ")
-      .append(Snapshot.class.getSimpleName())
-      .append(" s WHERE met.id=m.metricId AND m.snapshotId=s.id AND s.resourceId=r.id AND r.key=:kee AND s.status=:status AND s.qualifier<>:lib");
-    params.put("kee", resourceKey);
-    params.put("status", Snapshot.STATUS_PROCESSED);
-    params.put("lib", Qualifiers.LIBRARY);
-
-    sb.append(" AND m.characteristicId IS NULL");
-    sb.append(" AND m.personId IS NULL");
-    sb.append(" AND m.ruleId IS NULL AND m.rulePriority IS NULL");
-    if (metricKeys.length > 0) {
-      sb.append(" AND met.key IN (:metricKeys) ");
-      params.put("metricKeys", Arrays.asList(metricKeys));
-    }
-    sb.append(" AND s.last=true ");
-    sb.append(" ORDER BY s.createdAt ");
-
-    Query jpaQuery = session.createQuery(sb.toString());
-
-    for (Map.Entry<String, Object> entry : params.entrySet()) {
-      jpaQuery.setParameter(entry.getKey(), entry.getValue());
-    }
-    return jpaQuery.getResultList();
+    return ProjectRepositories.fromJson(serverClient.request(url));
   }
 
-  @CheckForNull
-  Date lastSnapshotCreationDate(String resourceKey) {
-    StringBuilder sb = new StringBuilder();
-    Map<String, Object> params = Maps.newHashMap();
-
-    sb.append("SELECT s.buildDate");
-    sb.append(" FROM ")
-      .append(ResourceModel.class.getSimpleName())
-      .append(" r, ")
-      .append(Snapshot.class.getSimpleName())
-      .append(" s WHERE s.resourceId=r.id AND r.key=:kee AND s.status=:status AND s.qualifier<>:lib");
-    params.put("kee", resourceKey);
-    params.put("status", Snapshot.STATUS_PROCESSED);
-    params.put("lib", Qualifiers.LIBRARY);
-
-    sb.append(" AND s.last=true ");
-
-    Query jpaQuery = session.createQuery(sb.toString());
-
-    for (Map.Entry<String, Object> entry : params.entrySet()) {
-      jpaQuery.setParameter(entry.getKey(), entry.getValue());
-    }
-    try {
-      return longToDate((Long) jpaQuery.getSingleResult());
-    } catch (NoResultException e) {
-      return null;
-    }
-  }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectScmRepositoryLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectScmRepositoryLoader.java
new file mode 100644 (file)
index 0000000..a5ee07d
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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.Maps;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.RequiresDB;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.batch.protocol.input.FileData;
+import org.sonar.batch.protocol.input.ProjectRepositories;
+
+import javax.annotation.CheckForNull;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static org.sonar.api.utils.DateUtils.longToDate;
+
+/**
+ * Waiting for SCM measure copy to be handled in computation stack we need to get previous measures from DB
+ */
+@RequiresDB
+public class ProjectScmRepositoryLoader implements BatchComponent {
+
+  private final DatabaseSession session;
+  private final ProjectReactor reactor;
+  private final ProjectRepositories ref;
+
+  public ProjectScmRepositoryLoader(DatabaseSession session, ProjectReactor reactor, ProjectRepositories ref) {
+    this.session = session;
+    this.reactor = reactor;
+    this.ref = ref;
+  }
+
+  public void complete() {
+    for (ProjectDefinition module : reactor.getProjects()) {
+
+      for (Entry<String, FileData> fileDataByPaths : ref.fileDataByPath(module.getKeyWithBranch()).entrySet()) {
+        String path = fileDataByPaths.getKey();
+        FileData fileData = fileDataByPaths.getValue();
+        String lastCommits = null;
+        String revisions = null;
+        String authors = null;
+        List<Object[]> measuresByKey = query(module.getKeyWithBranch() + ":" + path, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY, CoreMetrics.SCM_REVISIONS_BY_LINE_KEY,
+          CoreMetrics.SCM_AUTHORS_BY_LINE_KEY);
+        for (Object[] measureByKey : measuresByKey) {
+          if (measureByKey[0].equals(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY)) {
+            lastCommits = ((MeasureModel) measureByKey[1]).getData(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE);
+          } else if (measureByKey[0].equals(CoreMetrics.SCM_REVISIONS_BY_LINE_KEY)) {
+            revisions = ((MeasureModel) measureByKey[1]).getData(CoreMetrics.SCM_REVISIONS_BY_LINE);
+          } else if (measureByKey[0].equals(CoreMetrics.SCM_AUTHORS_BY_LINE_KEY)) {
+            authors = ((MeasureModel) measureByKey[1]).getData(CoreMetrics.SCM_AUTHORS_BY_LINE);
+          }
+        }
+        ref.addFileData(module.getKeyWithBranch(), path, new FileData(fileData.hash(), authors == null, lastCommits, revisions, authors));
+      }
+    }
+    ref.setLastAnalysisDate(lastSnapshotCreationDate(reactor.getRoot().getKeyWithBranch()));
+  }
+
+  private List<Object[]> query(String resourceKey, String... metricKeys) {
+    StringBuilder sb = new StringBuilder();
+    Map<String, Object> params = Maps.newHashMap();
+
+    sb.append("SELECT met.key, m");
+    sb.append(" FROM ")
+      .append(MeasureModel.class.getSimpleName())
+      .append(" m, ")
+      .append(Metric.class.getSimpleName())
+      .append(" met, ")
+      .append(ResourceModel.class.getSimpleName())
+      .append(" r, ")
+      .append(Snapshot.class.getSimpleName())
+      .append(" s WHERE met.id=m.metricId AND m.snapshotId=s.id AND s.resourceId=r.id AND r.key=:kee AND s.status=:status AND s.qualifier<>:lib");
+    params.put("kee", resourceKey);
+    params.put("status", Snapshot.STATUS_PROCESSED);
+    params.put("lib", Qualifiers.LIBRARY);
+
+    sb.append(" AND m.characteristicId IS NULL");
+    sb.append(" AND m.personId IS NULL");
+    sb.append(" AND m.ruleId IS NULL AND m.rulePriority IS NULL");
+    if (metricKeys.length > 0) {
+      sb.append(" AND met.key IN (:metricKeys) ");
+      params.put("metricKeys", Arrays.asList(metricKeys));
+    }
+    sb.append(" AND s.last=true ");
+    sb.append(" ORDER BY s.createdAt ");
+
+    Query jpaQuery = session.createQuery(sb.toString());
+
+    for (Map.Entry<String, Object> entry : params.entrySet()) {
+      jpaQuery.setParameter(entry.getKey(), entry.getValue());
+    }
+    return jpaQuery.getResultList();
+  }
+
+  @CheckForNull
+  Date lastSnapshotCreationDate(String resourceKey) {
+    StringBuilder sb = new StringBuilder();
+    Map<String, Object> params = Maps.newHashMap();
+
+    sb.append("SELECT s.buildDate");
+    sb.append(" FROM ")
+      .append(ResourceModel.class.getSimpleName())
+      .append(" r, ")
+      .append(Snapshot.class.getSimpleName())
+      .append(" s WHERE s.resourceId=r.id AND r.key=:kee AND s.status=:status AND s.qualifier<>:lib");
+    params.put("kee", resourceKey);
+    params.put("status", Snapshot.STATUS_PROCESSED);
+    params.put("lib", Qualifiers.LIBRARY);
+
+    sb.append(" AND s.last=true ");
+
+    Query jpaQuery = session.createQuery(sb.toString());
+
+    for (Map.Entry<String, Object> entry : params.entrySet()) {
+      jpaQuery.setParameter(entry.getKey(), entry.getValue());
+    }
+    try {
+      return longToDate((Long) jpaQuery.getSingleResult());
+    } catch (NoResultException e) {
+      return null;
+    }
+  }
+}
index 70792f4a36abdd9adc73625ddefa435360dda729..45b4700e795e797c005a96052afb43293bc5fc1d 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.batch.scan;
 import org.sonar.api.batch.bootstrap.ProjectBuilder;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
 import org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext;
+import org.sonar.batch.repository.ProjectScmRepositoryLoader;
 
 import javax.annotation.Nullable;
 
@@ -39,19 +40,30 @@ import javax.annotation.Nullable;
 public class ProjectReactorReady {
 
   private final ProjectReactor reactor;
-  private ProjectBuilder[] projectBuilders;
-  private ProjectExclusions exclusions;
-  private ProjectReactorValidator validator;
+  private final ProjectBuilder[] projectBuilders;
+  private final ProjectExclusions exclusions;
+  private final ProjectReactorValidator validator;
+  private final ProjectScmRepositoryLoader projectScmRepositoryLoader;
 
-  public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, @Nullable ProjectBuilder[] projectBuilders, ProjectReactorValidator validator) {
+  public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, @Nullable ProjectBuilder[] projectBuilders, ProjectReactorValidator validator,
+    @Nullable ProjectScmRepositoryLoader projectScmRepositoryLoader) {
     this.exclusions = exclusions;
     this.reactor = reactor;
     this.projectBuilders = projectBuilders;
     this.validator = validator;
+    this.projectScmRepositoryLoader = projectScmRepositoryLoader;
+  }
+
+  public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, @Nullable ProjectBuilder[] projectBuilders, ProjectReactorValidator validator) {
+    this(exclusions, reactor, projectBuilders, validator, null);
+  }
+
+  public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, ProjectReactorValidator validator, ProjectScmRepositoryLoader projectScmRepositoryLoader) {
+    this(exclusions, reactor, new ProjectBuilder[0], validator, projectScmRepositoryLoader);
   }
 
   public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, ProjectReactorValidator validator) {
-    this(exclusions, reactor, new ProjectBuilder[0], validator);
+    this(exclusions, reactor, new ProjectBuilder[0], validator, null);
   }
 
   public void start() {
@@ -67,5 +79,11 @@ public class ProjectReactorReady {
 
     // 3 Validate final reactor
     validator.validate(reactor);
+
+    // 4 Complete missing SCM information from project repositories
+    if (projectScmRepositoryLoader != null) {
+      projectScmRepositoryLoader.complete();
+    }
+
   }
 }
index df9a85a1d8512e06521566fded4dc5267d417d23..8edc824b1fdce2255d56e25035b822fe901f6232 100644 (file)
@@ -67,6 +67,7 @@ import org.sonar.batch.mediumtest.ScanTaskObservers;
 import org.sonar.batch.phases.GraphPersister;
 import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
 import org.sonar.batch.repository.ProjectRepositoriesProvider;
+import org.sonar.batch.repository.ProjectScmRepositoryLoader;
 import org.sonar.batch.rule.ActiveRulesProvider;
 import org.sonar.batch.rule.RulesProvider;
 import org.sonar.batch.scan.filesystem.InputPathCache;
@@ -204,6 +205,8 @@ public class ProjectScanContainer extends ComponentContainer {
       SourcePersister.class,
       ResourceKeyMigration.class,
 
+      ProjectScmRepositoryLoader.class,
+
       // Users
       DefaultUserFinder.class,
 
index 3cb1f9ba4624d06d480602d3e7c0476a74638e1a..a21220d057525998955bf2140aa4fd230b366ab6 100644 (file)
  */
 package org.sonar.batch.repository;
 
-import org.sonar.batch.repository.DefaultProjectRepositoriesLoader;
-
 import com.google.common.collect.Maps;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
-import org.sonar.api.database.DatabaseSession;
 import org.sonar.batch.bootstrap.DefaultAnalysisMode;
 import org.sonar.batch.bootstrap.ServerClient;
 import org.sonar.batch.bootstrap.TaskProperties;
 import org.sonar.batch.rule.ModuleQProfiles;
+
 import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -50,9 +47,8 @@ public class DefaultProjectRepositoriesLoaderTest {
   public void prepare() {
     serverClient = mock(ServerClient.class);
     analysisMode = mock(DefaultAnalysisMode.class);
-    loader = new DefaultProjectRepositoriesLoader(mock(DatabaseSession.class), serverClient, analysisMode);
+    loader = new DefaultProjectRepositoriesLoader(serverClient, analysisMode);
     loader = spy(loader);
-    doReturn(null).when(loader).lastSnapshotCreationDate(anyString());
     when(serverClient.request(anyString())).thenReturn("{}");
     taskProperties = new TaskProperties(Maps.<String, String>newHashMap(), "");
   }
index 08a80de01ee596c9430e043835bf111f45203d7f..8ab69d7fec78bbb618dd529eadf3833a77aba101 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.batch.scan;
 import org.junit.Test;
 import org.sonar.api.batch.bootstrap.ProjectBuilder;
 import org.sonar.api.batch.bootstrap.ProjectReactor;
+import org.sonar.batch.repository.ProjectScmRepositoryLoader;
 
 import static org.mockito.Mockito.mock;
 
@@ -30,13 +31,14 @@ public class ProjectReactorReadyTest {
   public void should_do_nothing() {
     // it's only a barrier
     ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class),
-        new ProjectBuilder[] {mock(ProjectBuilder.class)}, mock(ProjectReactorValidator.class));
+      new ProjectBuilder[] {mock(ProjectBuilder.class)}, mock(ProjectReactorValidator.class), mock(ProjectScmRepositoryLoader.class));
     barrier.start();
   }
 
   @Test
   public void project_builders_should_be_optional() {
-    ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class), mock(ProjectReactorValidator.class));
+    ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class), mock(ProjectReactorValidator.class),
+      mock(ProjectScmRepositoryLoader.class));
     barrier.start();
   }
 }