diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2015-02-09 16:05:47 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2015-02-09 16:11:27 +0100 |
commit | bd55e134575007c9d51f5b39513e9dec0f97fc6d (patch) | |
tree | 4e0393dc860615060ea85ab250e684c4475a93a2 | |
parent | 7526781e77eb8abd4fd08b1c9fc035ae093a286b (diff) | |
download | sonarqube-bd55e134575007c9d51f5b39513e9dec0f97fc6d.tar.gz sonarqube-bd55e134575007c9d51f5b39513e9dec0f97fc6d.zip |
SONAR-6169 Fix lifecycle issue
Previous blame should be loaded after ProjectBuilders are executed
6 files changed, 188 insertions, 134 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java index a4c455178f9..772f6923466 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoader.java @@ -19,36 +19,15 @@ */ 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 index 00000000000..a5ee07dd685 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ProjectScmRepositoryLoader.java @@ -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; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java index 70792f4a36a..45b4700e795 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java @@ -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(); + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index df9a85a1d85..8edc824b1fd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -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, diff --git a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java index 3cb1f9ba462..a21220d0575 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/repository/DefaultProjectRepositoriesLoaderTest.java @@ -19,20 +19,17 @@ */ 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(), ""); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java index 08a80de01ee..8ab69d7fec7 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java @@ -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(); } } |